Skip to content

Commit

Permalink
Effects: annotate functions in stdlib with contracts
Browse files Browse the repository at this point in the history
build.xml was also changed to incorporate contracts in
mock-runtime-for-tests.jar, because it is using Standard.kt, which, in
turn, has contract-annotated functions.

==========
Introduction of EffectSystem: 17/18
  • Loading branch information
dsavvinov committed Oct 12, 2017
1 parent 4434db4 commit fb03656
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 15 deletions.
3 changes: 3 additions & 0 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,9 @@
<copy file="${basedir}/libraries/stdlib/src/kotlin/jvm/JvmVersion.kt" todir="${output}/mock-runtime-src"/>
<copy file="${basedir}/libraries/stdlib/src/kotlin/util/Standard.kt" todir="${output}/mock-runtime-src"/>
<copy file="${basedir}/libraries/stdlib/src/kotlin/internal/Annotations.kt" todir="${output}/mock-runtime-src"/>
<!-- Contracts -->
<copy file="${basedir}/libraries/stdlib/src/kotlin/internal/contracts/ContractBuilder.kt" todir="${output}/mock-runtime-src"/>
<copy file="${basedir}/libraries/stdlib/src/kotlin/internal/contracts/Effect.kt" todir="${output}/mock-runtime-src"/>

<new-kotlinc output="${output}/classes/mock-runtime" moduleName="kotlin-stdlib">
<src>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package kotlin.test

import kotlin.internal.*
import kotlin.internal.contracts.*
import kotlin.reflect.KClass

/**
Expand All @@ -40,6 +41,7 @@ fun assertTrue(message: String? = null, block: () -> Boolean): Unit = assertTrue

/** Asserts that the expression is `true` with an optional [message]. */
fun assertTrue(actual: Boolean, message: String? = null) {
contract { returns() implies actual }
return asserter.assertTrue(message ?: "Expected value to be true.", actual)
}

Expand All @@ -48,6 +50,7 @@ fun assertFalse(message: String? = null, block: () -> Boolean): Unit = assertFal

/** Asserts that the expression is `false` with an optional [message]. */
fun assertFalse(actual: Boolean, message: String? = null) {
contract { returns() implies (!actual) }
return asserter.assertTrue(message ?: "Expected value to be false.", !actual)
}

Expand All @@ -63,12 +66,14 @@ fun <@OnlyInputTypes T> assertNotEquals(illegal: T, actual: T, message: String?

/** Asserts that the [actual] value is not `null`, with an optional [message]. */
fun <T : Any> assertNotNull(actual: T?, message: String? = null): T {
contract { returns() implies (actual != null) }
asserter.assertNotNull(message, actual)
return actual!!
}

/** Asserts that the [actual] value is not `null`, with an optional [message] and a function [block] to process the not-null value. */
fun <T : Any, R> assertNotNull(actual: T?, message: String? = null, block: (T) -> R) {
contract { returns() implies (actual != null) }
asserter.assertNotNull(message, actual)
if (actual != null) {
block(actual)
Expand Down
17 changes: 15 additions & 2 deletions libraries/stdlib/src/kotlin/text/Strings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package kotlin.text

import kotlin.comparisons.*
import kotlin.internal.contracts.*


/**
Expand Down Expand Up @@ -225,7 +226,13 @@ public fun String.padEnd(length: Int, padChar: Char = ' '): String
* Returns `true` if this nullable char sequence is either `null` or empty.
*/
@kotlin.internal.InlineOnly
public inline fun CharSequence?.isNullOrEmpty(): Boolean = this == null || this.length == 0
public inline fun CharSequence?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies (this@isNullOrEmpty != null)
}

return this == null || this.length == 0
}

/**
* Returns `true` if this char sequence is empty (contains no characters).
Expand Down Expand Up @@ -253,7 +260,13 @@ public inline fun CharSequence.isNotBlank(): Boolean = !isBlank()
* Returns `true` if this nullable char sequence is either `null` or empty or consists solely of whitespace characters.
*/
@kotlin.internal.InlineOnly
public inline fun CharSequence?.isNullOrBlank(): Boolean = this == null || this.isBlank()
public inline fun CharSequence?.isNullOrBlank(): Boolean {
contract {
returns(false) implies (this@isNullOrBlank != null)
}

return this == null || this.isBlank()
}

/**
* Iterator for characters of the given char sequence.
Expand Down
37 changes: 34 additions & 3 deletions libraries/stdlib/src/kotlin/util/Preconditions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@
@file:kotlin.jvm.JvmName("PreconditionsKt")
package kotlin

import kotlin.internal.contracts.*

/**
* Throws an [IllegalArgumentException] if the [value] is false.
*
* @sample samples.misc.Preconditions.failRequireWithLazyMessage
*/
@kotlin.internal.InlineOnly
public inline fun require(value: Boolean): Unit = require(value) { "Failed requirement." }
public inline fun require(value: Boolean): Unit {
contract {
returns() implies value
}
require(value) { "Failed requirement." }
}

/**
* Throws an [IllegalArgumentException] with the result of calling [lazyMessage] if the [value] is false.
Expand All @@ -17,6 +24,9 @@ public inline fun require(value: Boolean): Unit = require(value) { "Failed requi
*/
@kotlin.internal.InlineOnly
public inline fun require(value: Boolean, lazyMessage: () -> Any): Unit {
contract {
returns() implies value
}
if (!value) {
val message = lazyMessage()
throw IllegalArgumentException(message.toString())
Expand All @@ -27,7 +37,12 @@ public inline fun require(value: Boolean, lazyMessage: () -> Any): Unit {
* Throws an [IllegalArgumentException] if the [value] is null. Otherwise returns the not null value.
*/
@kotlin.internal.InlineOnly
public inline fun <T:Any> requireNotNull(value: T?): T = requireNotNull(value) { "Required value was null." }
public inline fun <T:Any> requireNotNull(value: T?): T {
contract {
returns() implies (value != null)
}
return requireNotNull(value) { "Required value was null." }
}

/**
* Throws an [IllegalArgumentException] with the result of calling [lazyMessage] if the [value] is null. Otherwise
Expand All @@ -37,6 +52,10 @@ public inline fun <T:Any> requireNotNull(value: T?): T = requireNotNull(value) {
*/
@kotlin.internal.InlineOnly
public inline fun <T:Any> requireNotNull(value: T?, lazyMessage: () -> Any): T {
contract {
returns() implies (value != null)
}

if (value == null) {
val message = lazyMessage()
throw IllegalArgumentException(message.toString())
Expand All @@ -51,7 +70,12 @@ public inline fun <T:Any> requireNotNull(value: T?, lazyMessage: () -> Any): T {
* @sample samples.misc.Preconditions.failCheckWithLazyMessage
*/
@kotlin.internal.InlineOnly
public inline fun check(value: Boolean): Unit = check(value) { "Check failed." }
public inline fun check(value: Boolean): Unit {
contract {
returns() implies value
}
check(value) { "Check failed." }
}

/**
* Throws an [IllegalStateException] with the result of calling [lazyMessage] if the [value] is false.
Expand All @@ -60,6 +84,9 @@ public inline fun check(value: Boolean): Unit = check(value) { "Check failed." }
*/
@kotlin.internal.InlineOnly
public inline fun check(value: Boolean, lazyMessage: () -> Any): Unit {
contract {
returns() implies value
}
if (!value) {
val message = lazyMessage()
throw IllegalStateException(message.toString())
Expand All @@ -83,6 +110,10 @@ public inline fun <T:Any> checkNotNull(value: T?): T = checkNotNull(value) { "Re
*/
@kotlin.internal.InlineOnly
public inline fun <T:Any> checkNotNull(value: T?, lazyMessage: () -> Any): T {
contract {
returns() implies (value != null)
}

if (value == null) {
val message = lazyMessage()
throw IllegalStateException(message.toString())
Expand Down
61 changes: 52 additions & 9 deletions libraries/stdlib/src/kotlin/util/Standard.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
@file:kotlin.jvm.JvmName("StandardKt")
package kotlin

import kotlin.internal.contracts.*

/**
* An exception is thrown to indicate that a method body remains to be implemented.
*/
Expand All @@ -28,52 +30,91 @@ public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An
* Calls the specified function [block] and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R = block()
public inline fun <R> run(block: () -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}

/**
* Calls the specified function [block] with `this` value as its receiver and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R = block()
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}

/**
* Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}

/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}

/**
* Calls the specified function [block] with `this` value as its argument and returns `this` value.
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}

/**
* Calls the specified function [block] with `this` value as its argument and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}

/**
* Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't.
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (predicate(this)) this else null
}

/**
* Returns `this` value if it _does not_ satisfy the given [predicate] or `null`, if it does.
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
return if (!predicate(this)) this else null
}

/**
* Executes the given function [action] specified number of [times].
Expand All @@ -82,7 +123,9 @@ public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predica
*/
@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
for (index in 0 until times) {
contract { callsInPlace(action) }

for (index in 0..times - 1) {
action(index)
}
}
4 changes: 3 additions & 1 deletion prepare/mock-runtime-for-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ val copySources by task<Copy> {
.include("kotlin/collections/TypeAliases.kt",
"kotlin/jvm/JvmVersion.kt",
"kotlin/util/Standard.kt",
"kotlin/internal/Annotations.kt")
"kotlin/internal/Annotations.kt",
"kotlin/internal/contracts/ContractBuilder.kt",
"kotlin/internal/contracts/Effect.kt")
into(File(buildDir, "src"))
}

Expand Down

0 comments on commit fb03656

Please sign in to comment.