diff --git a/libraries/stdlib/api/js-v1/kotlin.kt b/libraries/stdlib/api/js-v1/kotlin.kt index 96a946ad2b2..72b8f7f3e59 100644 --- a/libraries/stdlib/api/js-v1/kotlin.kt +++ b/libraries/stdlib/api/js-v1/kotlin.kt @@ -879,6 +879,11 @@ public inline fun kotlin.Long.toUShort(): kotlin.UShort @kotlin.internal.InlineOnly public inline fun kotlin.Short.toUShort(): kotlin.UShort +@kotlin.SinceKotlin(version = "1.8") +@kotlin.ExperimentalStdlibApi +@kotlin.internal.InlineOnly +public inline fun T.use(block: (T) -> R): R + public interface Annotation { } @@ -925,6 +930,12 @@ public open class AssertionError : kotlin.Error { public constructor AssertionError(message: kotlin.String?, cause: kotlin.Throwable?) } +@kotlin.SinceKotlin(version = "1.8") +@kotlin.ExperimentalStdlibApi +public interface AutoCloseable { + public abstract fun close(): kotlin.Unit +} + public final class Boolean : kotlin.Comparable { /*∆*/ @kotlin.internal.IntrinsicConstEvaluation public final infix fun and(other: kotlin.Boolean): kotlin.Boolean diff --git a/libraries/stdlib/api/js/kotlin.kt b/libraries/stdlib/api/js/kotlin.kt index 9b903519343..a1b44496984 100644 --- a/libraries/stdlib/api/js/kotlin.kt +++ b/libraries/stdlib/api/js/kotlin.kt @@ -839,6 +839,11 @@ public inline fun kotlin.Long.toUShort(): kotlin.UShort @kotlin.internal.InlineOnly public inline fun kotlin.Short.toUShort(): kotlin.UShort +@kotlin.SinceKotlin(version = "1.8") +@kotlin.ExperimentalStdlibApi +@kotlin.internal.InlineOnly +public inline fun T.use(block: (T) -> R): R + public interface Annotation { } @@ -882,6 +887,12 @@ public open class AssertionError : kotlin.Error { public constructor AssertionError(message: kotlin.String?, cause: kotlin.Throwable?) } +@kotlin.SinceKotlin(version = "1.8") +@kotlin.ExperimentalStdlibApi +public interface AutoCloseable { + public abstract fun close(): kotlin.Unit +} + public final class Boolean : kotlin.Comparable { public final infix fun and(other: kotlin.Boolean): kotlin.Boolean diff --git a/libraries/stdlib/jdk7/src/kotlin/AutoCloseable.kt b/libraries/stdlib/jdk7/src/kotlin/AutoCloseable.kt deleted file mode 100644 index 3c9fc678a20..00000000000 --- a/libraries/stdlib/jdk7/src/kotlin/AutoCloseable.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") -@file:JvmName("AutoCloseableKt") -@file:kotlin.jvm.JvmPackageName("kotlin.jdk7") -package kotlin - -import kotlin.contracts.* - -/** - * Executes the given [block] function on this resource and then closes it down correctly whether an exception - * is thrown or not. - * - * In case if the resource is being closed due to an exception occurred in [block], and the closing also fails with an exception, - * the latter is added to the [suppressed][java.lang.Throwable.addSuppressed] exceptions of the former. - * - * @param block a function to process this [AutoCloseable] resource. - * @return the result of [block] function invoked on this resource. - */ -@SinceKotlin("1.2") -@kotlin.internal.InlineOnly -public inline fun T.use(block: (T) -> R): R { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - var exception: Throwable? = null - try { - return block(this) - } catch (e: Throwable) { - exception = e - throw e - } finally { - this.closeFinally(exception) - } -} - -/** - * Closes this [AutoCloseable], suppressing possible exception or error thrown by [AutoCloseable.close] function when - * it's being closed due to some other [cause] exception occurred. - * - * The suppressed exception is added to the list of suppressed exceptions of [cause] exception. - */ -@SinceKotlin("1.2") -@PublishedApi -internal fun AutoCloseable?.closeFinally(cause: Throwable?) = when { - this == null -> {} - cause == null -> close() - else -> - try { - close() - } catch (closeException: Throwable) { - cause.addSuppressed(closeException) - } -} diff --git a/libraries/stdlib/jdk7/src/kotlin/AutoCloseableJVM.kt b/libraries/stdlib/jdk7/src/kotlin/AutoCloseableJVM.kt new file mode 100644 index 00000000000..027417eaabb --- /dev/null +++ b/libraries/stdlib/jdk7/src/kotlin/AutoCloseableJVM.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2022 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. + */ + +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") +@file:kotlin.jvm.JvmName("AutoCloseableKt") +@file:kotlin.jvm.JvmPackageName("kotlin.jdk7") + +package kotlin + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +// TODO: Introduce this typealias when Common AutoCloseable is stabilized. +// This typealias shadows existing java.lang.AutoCloseable, and the experimental status would require +// opt-in in exiting usages (that are w/o package prefix). Hence it would break previously compilable code. +//@Suppress("ACTUAL_WITHOUT_EXPECT") +//@SinceKotlin("1.8") +//public actual typealias AutoCloseable = java.lang.AutoCloseable + +@Suppress("ACTUAL_WITHOUT_EXPECT") +@SinceKotlin("1.2") +@kotlin.internal.InlineOnly +// TODO: remove java.lang package prefix when the kotlin.AutoCloseable typealias is introduced and KT-55392 is fixed. +// The prefix is currently needed for the current dokka to generate correct signature. +public actual inline fun T.use(block: (T) -> R): R { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + var exception: Throwable? = null + try { + return block(this) + } catch (e: Throwable) { + exception = e + throw e + } finally { + this.closeFinally(exception) + } +} + +@SinceKotlin("1.2") +@PublishedApi +internal fun java.lang.AutoCloseable?.closeFinally(cause: Throwable?) = when { + this == null -> {} + cause == null -> close() + else -> + try { + close() + } catch (closeException: Throwable) { + cause.addSuppressed(closeException) + } +} \ No newline at end of file diff --git a/libraries/stdlib/jdk7/test/UseAutoCloseableResourceJVMTest.kt b/libraries/stdlib/jdk7/test/UseAutoCloseableResourceJVMTest.kt new file mode 100644 index 00000000000..842d25e739e --- /dev/null +++ b/libraries/stdlib/jdk7/test/UseAutoCloseableResourceJVMTest.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2022 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. + */ + +package kotlin.jdk7.test + +import test.platformNull +import java.io.* +import kotlin.test.* + +class UseAutoCloseableResourceJVMTest { + + class Resource : AutoCloseable { + override fun close() { + error("Unreachable") + } + } + + @Test fun platformResourceOpFails() { + val resource = platformNull() + val e = assertFails { + resource.use { requireNotNull(it) } + } + assertTrue(e is IllegalArgumentException) + assertTrue(e.suppressed.isEmpty()) + } +} \ No newline at end of file diff --git a/libraries/stdlib/js/src/kotlin/AutoCloseableJs.kt b/libraries/stdlib/js/src/kotlin/AutoCloseableJs.kt new file mode 100644 index 00000000000..527aff81eaf --- /dev/null +++ b/libraries/stdlib/js/src/kotlin/AutoCloseableJs.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2022 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. + */ + +package kotlin + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +@SinceKotlin("1.8") +@ExperimentalStdlibApi +public actual interface AutoCloseable { + public actual fun close(): Unit +} + +@SinceKotlin("1.8") +@ExperimentalStdlibApi +@kotlin.internal.InlineOnly +public actual inline fun T.use(block: (T) -> R): R { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + var exception: Throwable? = null + try { + return block(this) + } catch (e: Throwable) { + exception = e + throw e + } finally { + this.closeFinally(exception) + } +} + +@ExperimentalStdlibApi +@SinceKotlin("1.8") +@PublishedApi +internal fun AutoCloseable?.closeFinally(cause: Throwable?) = when { + this == null -> {} + cause == null -> close() + else -> + try { + close() + } catch (closeException: Throwable) { + cause.addSuppressed(closeException) + } +} diff --git a/libraries/stdlib/native-wasm/src/kotlin/AutoCloseable.kt b/libraries/stdlib/native-wasm/src/kotlin/AutoCloseable.kt new file mode 100644 index 00000000000..86eb881d0c7 --- /dev/null +++ b/libraries/stdlib/native-wasm/src/kotlin/AutoCloseable.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2022 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. + */ + +package kotlin + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +@SinceKotlin("1.8") +@ExperimentalStdlibApi +public actual interface AutoCloseable { + public actual fun close(): Unit +} + +@SinceKotlin("1.8") +@ExperimentalStdlibApi +@kotlin.internal.InlineOnly +public actual inline fun T.use(block: (T) -> R): R { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + var exception: Throwable? = null + try { + return block(this) + } catch (e: Throwable) { + exception = e + throw e + } finally { + this.closeFinally(exception) + } +} + +@SinceKotlin("1.8") +@ExperimentalStdlibApi +@PublishedApi +internal fun AutoCloseable?.closeFinally(cause: Throwable?) = when { + this == null -> {} + cause == null -> close() + else -> + try { + close() + } catch (closeException: Throwable) { + cause.addSuppressed(closeException) + } +} \ No newline at end of file diff --git a/libraries/stdlib/src/kotlin/AutoCloseable.kt b/libraries/stdlib/src/kotlin/AutoCloseable.kt new file mode 100644 index 00000000000..52fefe1cfa3 --- /dev/null +++ b/libraries/stdlib/src/kotlin/AutoCloseable.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2010-2022 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. + */ + +package kotlin + +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + +/** + * A resource that can be closed or released. + */ +@Suppress("NO_ACTUAL_FOR_EXPECT") +@SinceKotlin("1.8") +@ExperimentalStdlibApi +public expect interface AutoCloseable { + /** + * Closes this resource. + * + * This function may throw, thus it is strongly recommended to use the [use] function instead, + * which closes this resource correctly whether an exception is thrown or not. + * + * Implementers of this interface should pay increased attention to cases where the close operation may fail. + * It is recommended that all underlying resources are closed and the resource internally is marked as closed + * before throwing an exception. Such a strategy ensures that the resources are released in a timely manner, + * and avoids many problems that could come up when the resource wraps, or is wrapped, by another resource. + * + * Note that calling this function more than once may have some visible side effect. + * However, implementers of this interface are strongly recommended to make this function idempotent. + */ + public fun close(): Unit +} + +/** + * Executes the given [block] function on this resource and then closes it down correctly whether an exception + * is thrown or not. + * + * In case if the resource is being closed due to an exception occurred in [block], and the closing also fails with an exception, + * the latter is added to the [suppressed][Throwable.addSuppressed] exceptions of the former. + * + * @param block a function to process this [AutoCloseable] resource. + * @return the result of [block] function invoked on this resource. + */ +@Suppress("NO_ACTUAL_FOR_EXPECT", "EXPECTED_DECLARATION_WITH_BODY") +@SinceKotlin("1.8") +@ExperimentalStdlibApi +@kotlin.internal.InlineOnly +public expect inline fun T.use(block: (T) -> R): R { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + error("Unreachable") +} diff --git a/libraries/stdlib/jdk7/test/UseAutoCloseableResourceTest.kt b/libraries/stdlib/test/autoCloseable/UseAutoCloseableResourceTest.kt similarity index 78% rename from libraries/stdlib/jdk7/test/UseAutoCloseableResourceTest.kt rename to libraries/stdlib/test/autoCloseable/UseAutoCloseableResourceTest.kt index e757079b096..6a0537e28e9 100644 --- a/libraries/stdlib/jdk7/test/UseAutoCloseableResourceTest.kt +++ b/libraries/stdlib/test/autoCloseable/UseAutoCloseableResourceTest.kt @@ -1,12 +1,10 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2022 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. */ -package kotlin.jdk7.test +package test.autoCloseable -import test.platformNull -import java.io.* import kotlin.test.* class UseAutoCloseableResourceTest { @@ -18,11 +16,13 @@ class UseAutoCloseableResourceTest { override fun close() { if (faultyClose) - throw IOException("Close failed") + throw ResourceCloseException("Close failed") isClosed = true } } + class ResourceCloseException(message: String) : Exception(message) + @Test fun success() { val resource = Resource() @@ -35,7 +35,7 @@ class UseAutoCloseableResourceTest { val e = assertFails { Resource(faultyClose = true).use { it.isClosed } } - assertTrue(e is IOException) + assertTrue(e is ResourceCloseException) } @Test fun opFailsCloseSuccess() { @@ -43,7 +43,7 @@ class UseAutoCloseableResourceTest { Resource().use { error("op fail") } } assertTrue(e is IllegalStateException) - assertTrue(e.suppressed.isEmpty()) + assertTrue(e.suppressedExceptions.isEmpty()) } @Test fun opFailsCloseFails() { @@ -51,7 +51,7 @@ class UseAutoCloseableResourceTest { Resource(faultyClose = true).use { error("op fail") } } assertTrue(e is IllegalStateException) - assertTrue(e.suppressed.single() is IOException) + assertTrue(e.suppressedExceptions.single() is ResourceCloseException) } @Test fun opFailsCloseFailsTwice() { @@ -63,9 +63,9 @@ class UseAutoCloseableResourceTest { } } assertTrue(e is IllegalStateException) - val suppressed = e.suppressed + val suppressed = e.suppressedExceptions assertEquals(2, suppressed.size) - assertTrue(suppressed.all { it is IOException }) + assertTrue(suppressed.all { it is ResourceCloseException }) } @Test fun nonLocalReturnInBlock() { @@ -99,19 +99,9 @@ class UseAutoCloseableResourceTest { resource.use { requireNotNull(it) } } assertTrue(e is IllegalArgumentException) - assertTrue(e.suppressed.isEmpty()) + assertTrue(e.suppressedExceptions.isEmpty()) } - @Test fun platformResourceOpFails() { - val resource = platformNull() - val e = assertFails { - resource.use { requireNotNull(it) } - } - assertTrue(e is IllegalArgumentException) - assertTrue(e.suppressed.isEmpty()) - } - - @Test fun contractCallsInPlace() { val i: Int @@ -122,4 +112,4 @@ class UseAutoCloseableResourceTest { } assertEquals(1, i) } -} +} \ No newline at end of file