diff --git a/libraries/stdlib/api/js/kotlin.kt b/libraries/stdlib/api/js/kotlin.kt index a542a9ed48c..4f0e60d4fb3 100644 --- a/libraries/stdlib/api/js/kotlin.kt +++ b/libraries/stdlib/api/js/kotlin.kt @@ -12,6 +12,10 @@ kotlin.reflect.KProperty0<*>.isInitialized: kotlin.Boolean { get; } @kotlin.SinceKotlin(version = "1.4") public val kotlin.Throwable.suppressedExceptions: kotlin.collections.List { get; } +@kotlin.SinceKotlin(version = "2.0") +@kotlin.internal.InlineOnly +public inline fun AutoCloseable(crossinline closeAction: () -> kotlin.Unit): kotlin.AutoCloseable + @kotlin.SinceKotlin(version = "1.5") @kotlin.WasExperimental(markerClass = {kotlin.ExperimentalStdlibApi::class}) @kotlin.internal.InlineOnly diff --git a/libraries/stdlib/jdk7/src/kotlin/AutoCloseableJVM.kt b/libraries/stdlib/jdk7/src/kotlin/AutoCloseableJVM.kt index 1d16b27a76b..dded6542661 100644 --- a/libraries/stdlib/jdk7/src/kotlin/AutoCloseableJVM.kt +++ b/libraries/stdlib/jdk7/src/kotlin/AutoCloseableJVM.kt @@ -16,6 +16,11 @@ import kotlin.contracts.contract @SinceKotlin("2.0") public actual typealias AutoCloseable = java.lang.AutoCloseable +@Suppress("ACTUAL_WITHOUT_EXPECT") +@SinceKotlin("2.0") +@kotlin.internal.InlineOnly +public actual inline fun AutoCloseable(crossinline closeAction: () -> Unit): AutoCloseable = java.lang.AutoCloseable { closeAction() } + @Suppress("ACTUAL_WITHOUT_EXPECT") @SinceKotlin("1.2") @kotlin.internal.InlineOnly diff --git a/libraries/stdlib/js/src/kotlin/AutoCloseableJs.kt b/libraries/stdlib/js/src/kotlin/AutoCloseableJs.kt index 0fe241e33fb..f194f081a32 100644 --- a/libraries/stdlib/js/src/kotlin/AutoCloseableJs.kt +++ b/libraries/stdlib/js/src/kotlin/AutoCloseableJs.kt @@ -14,6 +14,12 @@ public actual interface AutoCloseable { public actual fun close(): Unit } +@SinceKotlin("2.0") +@kotlin.internal.InlineOnly +public actual inline fun AutoCloseable(crossinline closeAction: () -> Unit): AutoCloseable = object : AutoCloseable { + override fun close() = closeAction() +} + @SinceKotlin("2.0") @WasExperimental(ExperimentalStdlibApi::class) @kotlin.internal.InlineOnly diff --git a/libraries/stdlib/native-wasm/src/kotlin/AutoCloseable.kt b/libraries/stdlib/native-wasm/src/kotlin/AutoCloseable.kt index 69a194c2931..25bc6f7810f 100644 --- a/libraries/stdlib/native-wasm/src/kotlin/AutoCloseable.kt +++ b/libraries/stdlib/native-wasm/src/kotlin/AutoCloseable.kt @@ -14,6 +14,12 @@ public actual interface AutoCloseable { public actual fun close(): Unit } +@SinceKotlin("2.0") +@kotlin.internal.InlineOnly +public actual inline fun AutoCloseable(crossinline closeAction: () -> Unit): AutoCloseable = object : AutoCloseable { + override fun close() = closeAction() +} + @SinceKotlin("2.0") @WasExperimental(ExperimentalStdlibApi::class) @kotlin.internal.InlineOnly diff --git a/libraries/stdlib/src/kotlin/AutoCloseable.kt b/libraries/stdlib/src/kotlin/AutoCloseable.kt index 73626746874..d3940c1aaf0 100644 --- a/libraries/stdlib/src/kotlin/AutoCloseable.kt +++ b/libraries/stdlib/src/kotlin/AutoCloseable.kt @@ -32,6 +32,39 @@ public expect interface AutoCloseable { public fun close(): Unit } +/** + * Returns an [AutoCloseable] instance that executes the specified [closeAction] + * upon invocation of its [`close()`][AutoCloseable.close] function. + * + * This function allows specifying custom cleanup actions for resources. + * + * Note that each invocation of the `close()` function on the returned `AutoCloseable` instance executes the [closeAction]. + * Therefore, implementers are strongly recommended to make the [closeAction] idempotent, or to prevent multiple invocations. + * + * Example: + * + * ```kotlin + * val autoCloseable = AutoCloseable { + * // Cleanup action, e.g., closing a file or releasing a network connection + * Logger.log("Releasing the network connection.") + * networkConnection.release() + * } + * + * // Now you can pass the autoCloseable to a function or use it directly. + * autoCloseable.use { + * // Use the connection, which will be automatically released when this scope finishes. + * val content = networkConnection.readContent() + * Logger.log("Network connection content: $content") + * } + * ``` + * + * @See AutoCloseable.use + */ +@Suppress("NO_ACTUAL_FOR_EXPECT") +@SinceKotlin("2.0") +@kotlin.internal.InlineOnly +public expect inline fun AutoCloseable(crossinline closeAction: () -> Unit): AutoCloseable + /** * Executes the given [block] function on this resource and then closes it down correctly whether an exception * is thrown or not. diff --git a/libraries/stdlib/test/autoCloseable/AutoCloseableConstructorFunctionTest.kt b/libraries/stdlib/test/autoCloseable/AutoCloseableConstructorFunctionTest.kt new file mode 100644 index 00000000000..49b3c10fc3c --- /dev/null +++ b/libraries/stdlib/test/autoCloseable/AutoCloseableConstructorFunctionTest.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2024 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 test.autoCloseable + +import kotlin.test.* + +class AutoCloseableConstructorFunctionTest { + class ResourceCloseException : Exception() + + @Test + fun success() { + var isClosed = false + val resource = AutoCloseable(closeAction = { isClosed = true }) + val result = resource.use { "ok" } + assertEquals("ok", result) + assertTrue(isClosed) + } + + @Test + fun closeFails() { + assertFailsWith { + AutoCloseable { throw ResourceCloseException() }.close() + } + } + + @Test + fun multipleCloseInvocations() { + var counter = 0 + val resource = AutoCloseable { counter++ } + resource.use {} + assertEquals(1, counter) + resource.close() + resource.close() + assertEquals(3, counter) + } +}