Add EXACTLY_ONCE contract to functions that call their lambda parameter once

KT-35972
This commit is contained in:
Ilya Gorbunov
2020-07-01 18:47:44 +03:00
parent c85432b2f9
commit 1a32fdf6d7
5 changed files with 53 additions and 21 deletions
@@ -29,7 +29,10 @@ val asserter: Asserter
internal var _asserter: Asserter? = null internal var _asserter: Asserter? = null
/** Asserts that the given [block] returns `true`. */ /** Asserts that the given [block] returns `true`. */
fun assertTrue(message: String? = null, block: () -> Boolean): Unit = assertTrue(block(), message) fun assertTrue(message: String? = null, block: () -> Boolean) {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
assertTrue(block(), message)
}
/** Asserts that the expression is `true` with an optional [message]. */ /** Asserts that the expression is `true` with an optional [message]. */
fun assertTrue(actual: Boolean, message: String? = null) { fun assertTrue(actual: Boolean, message: String? = null) {
@@ -38,7 +41,10 @@ fun assertTrue(actual: Boolean, message: String? = null) {
} }
/** Asserts that the given [block] returns `false`. */ /** Asserts that the given [block] returns `false`. */
fun assertFalse(message: String? = null, block: () -> Boolean): Unit = assertFalse(block(), message) fun assertFalse(message: String? = null, block: () -> Boolean) {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
assertFalse(block(), message)
}
/** Asserts that the expression is `false` with an optional [message]. */ /** Asserts that the expression is `false` with an optional [message]. */
fun assertFalse(actual: Boolean, message: String? = null) { fun assertFalse(actual: Boolean, message: String? = null) {
@@ -105,11 +111,13 @@ fun fail(message: String? = null, cause: Throwable? = null): Nothing {
/** Asserts that given function [block] returns the given [expected] value. */ /** Asserts that given function [block] returns the given [expected] value. */
fun <@OnlyInputTypes T> expect(expected: T, block: () -> T) { fun <@OnlyInputTypes T> expect(expected: T, block: () -> T) {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
assertEquals(expected, block()) assertEquals(expected, block())
} }
/** Asserts that given function [block] returns the given [expected] value and use the given [message] if it fails. */ /** Asserts that given function [block] returns the given [expected] value and use the given [message] if it fails. */
fun <@OnlyInputTypes T> expect(expected: T, message: String?, block: () -> T) { fun <@OnlyInputTypes T> expect(expected: T, message: String?, block: () -> T) {
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
assertEquals(expected, block(), message) assertEquals(expected, block(), message)
} }
@@ -6,19 +6,25 @@
package kotlinx.dom package kotlinx.dom
import org.w3c.dom.* import org.w3c.dom.*
import kotlin.contracts.*
/** /**
* Creates a new element with the specified [name]. * Creates a new element with the specified [name].
* *
* The element is initialized with the specified [init] function. * The element is initialized with the specified [init] function.
*/ */
public fun Document.createElement(name: String, init: Element.() -> Unit): Element = createElement(name).apply(init) public fun Document.createElement(name: String, init: Element.() -> Unit): Element {
contract { callsInPlace(init, InvocationKind.EXACTLY_ONCE) }
return createElement(name).apply(init)
}
/** /**
* Appends a newly created element with the specified [name] to this element. * Appends a newly created element with the specified [name] to this element.
* *
* The element is initialized with the specified [init] function. * The element is initialized with the specified [init] function.
*/ */
public fun Element.appendElement(name: String, init: Element.() -> Unit): Element = public fun Element.appendElement(name: String, init: Element.() -> Unit): Element {
ownerDocument!!.createElement(name, init).also { appendChild(it) } contract { callsInPlace(init, InvocationKind.EXACTLY_ONCE) }
return ownerDocument!!.createElement(name, init).also { appendChild(it) }
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -8,7 +8,7 @@ package kotlin.concurrent
import java.util.concurrent.locks.Lock import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock
import java.util.concurrent.CountDownLatch import kotlin.contracts.*
/** /**
* Executes the given [action] under this lock. * Executes the given [action] under this lock.
@@ -16,6 +16,7 @@ import java.util.concurrent.CountDownLatch
*/ */
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <T> Lock.withLock(action: () -> T): T { public inline fun <T> Lock.withLock(action: () -> T): T {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
lock() lock()
try { try {
return action() return action()
@@ -30,6 +31,7 @@ public inline fun <T> Lock.withLock(action: () -> T): T {
*/ */
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <T> ReentrantReadWriteLock.read(action: () -> T): T { public inline fun <T> ReentrantReadWriteLock.read(action: () -> T): T {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
val rl = readLock() val rl = readLock()
rl.lock() rl.lock()
try { try {
@@ -54,6 +56,7 @@ public inline fun <T> ReentrantReadWriteLock.read(action: () -> T): T {
*/ */
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun <T> ReentrantReadWriteLock.write(action: () -> T): T { public inline fun <T> ReentrantReadWriteLock.write(action: () -> T): T {
contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
val rl = readLock() val rl = readLock()
val readCount = if (writeHoldCount == 0) readHoldCount else 0 val readCount = if (writeHoldCount == 0) readHoldCount else 0
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -8,6 +8,8 @@
package kotlin.text package kotlin.text
import kotlin.contracts.*
/** /**
* A mutable sequence of characters. * A mutable sequence of characters.
* *
@@ -399,8 +401,10 @@ public inline fun StringBuilder.append(obj: Any?): StringBuilder = this.append(o
* and then converting it to [String]. * and then converting it to [String].
*/ */
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun buildString(builderAction: StringBuilder.() -> Unit): String = public inline fun buildString(builderAction: StringBuilder.() -> Unit): String {
StringBuilder().apply(builderAction).toString() contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
return StringBuilder().apply(builderAction).toString()
}
/** /**
* Builds new string by populating newly created [StringBuilder] initialized with the given [capacity] * Builds new string by populating newly created [StringBuilder] initialized with the given [capacity]
@@ -408,8 +412,10 @@ public inline fun buildString(builderAction: StringBuilder.() -> Unit): String =
*/ */
@SinceKotlin("1.1") @SinceKotlin("1.1")
@kotlin.internal.InlineOnly @kotlin.internal.InlineOnly
public inline fun buildString(capacity: Int, builderAction: StringBuilder.() -> Unit): String = public inline fun buildString(capacity: Int, builderAction: StringBuilder.() -> Unit): String {
StringBuilder(capacity).apply(builderAction).toString() contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
return StringBuilder(capacity).apply(builderAction).toString()
}
/** /**
* Appends all arguments to the given StringBuilder. * Appends all arguments to the given StringBuilder.
+18 -9
View File
@@ -1,10 +1,11 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
package kotlin.time package kotlin.time
import kotlin.contracts.*
import kotlin.math.abs import kotlin.math.abs
@OptIn(ExperimentalTime::class) @OptIn(ExperimentalTime::class)
@@ -104,8 +105,10 @@ public inline class Duration internal constructor(internal val value: Double) :
* If the value doesn't fit in [Int] range, i.e. it's greater than [Int.MAX_VALUE] or less than [Int.MIN_VALUE], * If the value doesn't fit in [Int] range, i.e. it's greater than [Int.MAX_VALUE] or less than [Int.MIN_VALUE],
* it is coerced into that range. * it is coerced into that range.
*/ */
public inline fun <T> toComponents(action: (days: Int, hours: Int, minutes: Int, seconds: Int, nanoseconds: Int) -> T): T = public inline fun <T> toComponents(action: (days: Int, hours: Int, minutes: Int, seconds: Int, nanoseconds: Int) -> T): T {
action(inDays.toInt(), hoursComponent, minutesComponent, secondsComponent, nanosecondsComponent) contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return action(inDays.toInt(), hoursComponent, minutesComponent, secondsComponent, nanosecondsComponent)
}
/** /**
* Splits this duration into hours, minutes, seconds, and nanoseconds and executes the given [action] with these components. * Splits this duration into hours, minutes, seconds, and nanoseconds and executes the given [action] with these components.
@@ -118,8 +121,10 @@ public inline class Duration internal constructor(internal val value: Double) :
* If the value doesn't fit in [Int] range, i.e. it's greater than [Int.MAX_VALUE] or less than [Int.MIN_VALUE], * If the value doesn't fit in [Int] range, i.e. it's greater than [Int.MAX_VALUE] or less than [Int.MIN_VALUE],
* it is coerced into that range. * it is coerced into that range.
*/ */
public inline fun <T> toComponents(action: (hours: Int, minutes: Int, seconds: Int, nanoseconds: Int) -> T): T = public inline fun <T> toComponents(action: (hours: Int, minutes: Int, seconds: Int, nanoseconds: Int) -> T): T {
action(inHours.toInt(), minutesComponent, secondsComponent, nanosecondsComponent) contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return action(inHours.toInt(), minutesComponent, secondsComponent, nanosecondsComponent)
}
/** /**
* Splits this duration into minutes, seconds, and nanoseconds and executes the given [action] with these components. * Splits this duration into minutes, seconds, and nanoseconds and executes the given [action] with these components.
@@ -131,8 +136,10 @@ public inline class Duration internal constructor(internal val value: Double) :
* If the value doesn't fit in [Int] range, i.e. it's greater than [Int.MAX_VALUE] or less than [Int.MIN_VALUE], * If the value doesn't fit in [Int] range, i.e. it's greater than [Int.MAX_VALUE] or less than [Int.MIN_VALUE],
* it is coerced into that range. * it is coerced into that range.
*/ */
public inline fun <T> toComponents(action: (minutes: Int, seconds: Int, nanoseconds: Int) -> T): T = public inline fun <T> toComponents(action: (minutes: Int, seconds: Int, nanoseconds: Int) -> T): T {
action(inMinutes.toInt(), secondsComponent, nanosecondsComponent) contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return action(inMinutes.toInt(), secondsComponent, nanosecondsComponent)
}
/** /**
* Splits this duration into seconds, and nanoseconds and executes the given [action] with these components. * Splits this duration into seconds, and nanoseconds and executes the given [action] with these components.
@@ -143,8 +150,10 @@ public inline class Duration internal constructor(internal val value: Double) :
* If the value doesn't fit in [Long] range, i.e. it's greater than [Long.MAX_VALUE] or less than [Long.MIN_VALUE], * If the value doesn't fit in [Long] range, i.e. it's greater than [Long.MAX_VALUE] or less than [Long.MIN_VALUE],
* it is coerced into that range. * it is coerced into that range.
*/ */
public inline fun <T> toComponents(action: (seconds: Long, nanoseconds: Int) -> T): T = public inline fun <T> toComponents(action: (seconds: Long, nanoseconds: Int) -> T): T {
action(inSeconds.toLong(), nanosecondsComponent) contract { callsInPlace(action, InvocationKind.EXACTLY_ONCE) }
return action(inSeconds.toLong(), nanosecondsComponent)
}
@PublishedApi @PublishedApi
internal val hoursComponent: Int get() = (inHours % 24).toInt() internal val hoursComponent: Int get() = (inHours % 24).toInt()