From 53db2f0d71d1bc3b2022612fed8fb425c8a30469 Mon Sep 17 00:00:00 2001 From: Andrew Golovashevich Date: Fri, 5 Sep 2025 19:10:41 +0300 Subject: [PATCH] POSIX error asserters and minor improvements in other classes --- lib/closeable-state | 2 +- .../posix/PosixApiException.kt | 20 ++++--- .../posix/error_asserters.kt | 56 +++++++++++++++++++ .../windows/WindowsApiException.kt | 3 + .../windows/WindowsHandleWrapper.kt | 18 ++++++ .../windows/error_asserters.kt | 3 +- 6 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 src/posixMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/posix/error_asserters.kt diff --git a/lib/closeable-state b/lib/closeable-state index 4b799c0..e578517 160000 --- a/lib/closeable-state +++ b/lib/closeable-state @@ -1 +1 @@ -Subproject commit 4b799c0ecfac0d501ce335147c2ed021e5d4bea9 +Subproject commit e57851782134ca0613646efdf7b44d2e5a23de08 diff --git a/src/posixMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/posix/PosixApiException.kt b/src/posixMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/posix/PosixApiException.kt index 85fcfd7..a4ef8f7 100644 --- a/src/posixMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/posix/PosixApiException.kt +++ b/src/posixMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/posix/PosixApiException.kt @@ -2,7 +2,7 @@ package ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.posix import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.toKStringFromUtf8 -import platform.posix.errno +import platform.posix.errno as native_errnoVar import platform.posix.strerror import ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.OsException @@ -31,20 +31,22 @@ public class PosixApiException : OsException { } override fun toString(): String = - "" + "" public companion object { - public fun throwFromLastWindowsErr(): Nothing { - throw this.formatFromLastWindowsErr() + public fun getLastErrno(): Int = native_errnoVar + + public fun throwFromLastPosixErr(): Nothing { + throw this.formatFromLastPosixErr() } - public fun throwFromWindowsErrCode(code: Int): Nothing { - throw this.formatFromWindowsErrCode(code) + public fun throwFromPosixErrCode(code: Int): Nothing { + throw this.formatFromPosixErrCode(code) } - public fun formatFromLastWindowsErr(): PosixApiException = this.formatFromWindowsErrCode(errno) + public fun formatFromLastPosixErr(): PosixApiException = this.formatFromPosixErrCode(native_errnoVar) - public fun formatFromWindowsErrCode(code: Int): PosixApiException { + public fun formatFromPosixErrCode(code: Int): PosixApiException { var err = PosixApiException(errno = code, nativeMessage = null, customMessage = "[errno=${code}]") try { val nativeMessage = this.getNativeMessageByErrno(code) @@ -61,7 +63,7 @@ public class PosixApiException : OsException { if (raw != null) { return raw.toKStringFromUtf8() } else { - val strerrErrno = errno + val strerrErrno = native_errnoVar throw PosixApiException(errno = strerrErrno, nativeMessage = null, "[errno=${strerrErrno}]") } } diff --git a/src/posixMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/posix/error_asserters.kt b/src/posixMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/posix/error_asserters.kt new file mode 100644 index 0000000..ee0adb5 --- /dev/null +++ b/src/posixMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/posix/error_asserters.kt @@ -0,0 +1,56 @@ +@file:OptIn(ExperimentalContracts::class) + +package ru.landgrafhomyak.utility.kotlin_native_interop_stdlib_0.posix + +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlinx.cinterop.ExperimentalForeignApi + + +private fun _checkErrnoAndThrow(): Nothing { + val code = PosixApiException.getLastErrno() + if (code == 0) + throw RuntimeException("Function returned error status, but no error set") + PosixApiException.throwFromLastPosixErr() +} + +public fun Int.zeroToWinApiErr(): Int { + if (this != 0) + return this + + _checkErrnoAndThrow() +} + +public fun zeroToWinApiErr(call: () -> Int): Int { + contract { + callsInPlace(call, InvocationKind.EXACTLY_ONCE) + } + + val status = call() + if (status != 0) + return status + + _checkErrnoAndThrow() +} + +@OptIn(ExperimentalForeignApi::class) +public fun R?.nullToWinApiErr(): R { + if (this != null) + return this + + _checkErrnoAndThrow() +} + +@OptIn(ExperimentalForeignApi::class) +public fun nullToWinApiErr(call: () -> R?): R { + contract { + callsInPlace(call, InvocationKind.EXACTLY_ONCE) + } + + val ref = call() + if (ref != null) + return ref + + _checkErrnoAndThrow() +} \ No newline at end of file diff --git a/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsApiException.kt b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsApiException.kt index 106a22d..33b93fb 100644 --- a/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsApiException.kt +++ b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsApiException.kt @@ -9,6 +9,7 @@ import kotlinx.cinterop.ptr import kotlinx.cinterop.reinterpret import kotlinx.cinterop.toKString import kotlinx.cinterop.value +import platform.posix.errno import platform.windows.FORMAT_MESSAGE_ALLOCATE_BUFFER import platform.windows.FORMAT_MESSAGE_FROM_SYSTEM import platform.windows.FORMAT_MESSAGE_IGNORE_INSERTS @@ -46,6 +47,8 @@ public class WindowsApiException : OsException { "" public companion object { + public fun getLastErrno(): UInt = GetLastError() + public fun throwFromLastWindowsErr(): Nothing { throw this.formatFromLastWindowsErr() } diff --git a/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsHandleWrapper.kt b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsHandleWrapper.kt index ae1ff9a..c3c9ea5 100644 --- a/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsHandleWrapper.kt +++ b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/WindowsHandleWrapper.kt @@ -9,6 +9,7 @@ import platform.windows.HANDLE import ru.landgrafhomyak.utility.closeable_state_1.CloseableState import ru.landgrafhomyak.utility.closeable_state_1.HandleWrapper import ru.landgrafhomyak.utility.closeable_state_1.ManualStateManipulation +import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1 @OptIn(ExperimentalForeignApi::class) public class WindowsHandleWrapper( @@ -32,4 +33,21 @@ public class WindowsHandleWrapper( this._orig.close() zeroToWinApiErr { CloseHandle(this._orig.handle) } } + + public companion object { + @OptIn(ExperimentalContracts::class, ManualStateManipulation::class) + public fun autoClosed(state: () -> CloseableState, constructor: () -> HANDLE, block: (WindowsHandleWrapper) -> R) { + contract { + callsInPlace(state, InvocationKind.EXACTLY_ONCE) + callsInPlace(constructor, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + + val wrapper = WindowsHandleWrapper(constructor(), state()) + safeAutoClose1( + action = { block(wrapper) }, + finally = { wrapper.close() } + ) + } + } } \ No newline at end of file diff --git a/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/error_asserters.kt b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/error_asserters.kt index 8d7abbb..ab8a65f 100644 --- a/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/error_asserters.kt +++ b/src/windowsMain/kotlin/ru/landgrafhomyak/utility/kotlin_native_interop_stdlib_0/windows/error_asserters.kt @@ -7,11 +7,10 @@ import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlinx.cinterop.ExperimentalForeignApi import platform.windows.ERROR_SUCCESS -import platform.windows.GetLastError import platform.windows.NULL private fun _checkErrnoAndThrow(): Nothing { - val code = GetLastError() + val code = WindowsApiException.getLastErrno() if (code == ERROR_SUCCESS.toUInt()) throw RuntimeException("Function returned error status, but no error set") WindowsApiException.throwFromLastWindowsErr()