Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
7d947fb9e3 | |||
0b241ea7e2 | |||
25cb516d0f | |||
099c3fad26 | |||
28ab658dd2 | |||
529508bf74 | |||
758bc31a85 | |||
c154fc7610 | |||
ee93e0b5b3 | |||
88c8da21e4 | |||
ef1d72ca10 | |||
87386193ee | |||
b9e02d68f8 | |||
b2a5ac9292 | |||
6f9d3bdd32 | |||
586e825693 | |||
fc4bd9eb89 |
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/.idea/
|
||||||
|
gradle/
|
||||||
|
.gradle/
|
||||||
|
build/
|
||||||
|
*.class
|
||||||
|
*.jar
|
||||||
|
/out/
|
||||||
|
/gradlew*
|
||||||
|
.kotlin/
|
101
build.gradle.kts
Normal file
101
build.gradle.kts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
|
||||||
|
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.*
|
||||||
|
import ru.landgrafhomyak.kotlin.kmp_gradle_build_helper.plugin.xomrk
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven("https://maven.landgrafhomyak.ru/")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath("ru.landgrafhomyak.kotlin:kotlin-mpp-gradle-build:v0.3k2.1.10")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "ru.landgrafhomyak.utility"
|
||||||
|
version = "0.5"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
xomrk {
|
||||||
|
kotlin {
|
||||||
|
setCompatibilityWithKotlin(KotlinVersion.KOTLIN_2_0)
|
||||||
|
optInContracts()
|
||||||
|
noWarnExpectActual()
|
||||||
|
warningsAsErrors()
|
||||||
|
|
||||||
|
defineAllMultiplatformTargets()
|
||||||
|
|
||||||
|
jvmToolchain(8)
|
||||||
|
jvm {
|
||||||
|
withJava()
|
||||||
|
|
||||||
|
compilations.configureEach {
|
||||||
|
compileJavaTaskProvider?.configure {
|
||||||
|
targetCompatibility = "1.8"
|
||||||
|
}
|
||||||
|
compileTaskProvider.configure {
|
||||||
|
compilerOptions {
|
||||||
|
jvmTarget = JvmTarget.JVM_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named { t -> t == "${this@jvm.name}Test" }.configureEach {
|
||||||
|
this as Test
|
||||||
|
useTestNG()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
// if use kotlin("stdlib") gitea ui brokes at paragraph with dependency versions
|
||||||
|
val kotlinStdlibDependency = "org.jetbrains.kotlin:kotlin-stdlib:${this@kotlin.coreLibrariesVersion}"
|
||||||
|
|
||||||
|
val commonMain by getting {
|
||||||
|
dependencies {
|
||||||
|
compileOnly(kotlinStdlibDependency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val jvmMain by getting {
|
||||||
|
dependsOn(commonMain)
|
||||||
|
dependencies {
|
||||||
|
compileOnly(kotlinStdlibDependency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val nonJvmMain by creating {
|
||||||
|
dependsOn(commonMain)
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlinStdlibDependency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jvmTest {
|
||||||
|
dependencies {
|
||||||
|
implementation("org.testng:testng:7.5.1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configureEach {
|
||||||
|
when {
|
||||||
|
// commonMain !in dependsOn -> return@configureEach
|
||||||
|
!name.endsWith("Main") -> return@configureEach
|
||||||
|
this@configureEach === commonMain -> return@configureEach
|
||||||
|
this@configureEach === jvmMain -> return@configureEach
|
||||||
|
this@configureEach === nonJvmMain -> return@configureEach
|
||||||
|
}
|
||||||
|
dependsOn(nonJvmMain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
defineXomrkGiteaMavenRepo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
gradle.properties
Normal file
6
gradle.properties
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
kotlin.stdlib.default.dependency=false
|
||||||
|
kotlin.mpp.applyDefaultHierarchyTemplate=false
|
||||||
|
kotlin.native.enableKlibsCrossCompilation=true
|
||||||
|
# compileOnly dependencies from commonMain still throw warning
|
||||||
|
kotlin.suppressGradlePluginWarnings=IncorrectCompileOnlyDependencyWarning
|
||||||
|
kotlin.js.stdlib.dom.api.included=false
|
1
settings.gradle.kts
Normal file
1
settings.gradle.kts
Normal file
@ -0,0 +1 @@
|
|||||||
|
rootProject.name = "highlevel-try-finally"
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.landgrafhomyak.utility.highlevel_try_finally
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal expect object ExceptionsKt {
|
||||||
|
@PublishedApi
|
||||||
|
internal fun addSuppressed(e1: Throwable?, e2: Throwable?)
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package ru.landgrafhomyak.utility.highlevel_try_finally
|
||||||
|
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
|
class TryFinallyChainScope @PublishedApi internal constructor() {
|
||||||
|
@PublishedApi
|
||||||
|
internal var _actualException: Throwable? = null
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun _throw() {
|
||||||
|
this._actualException?.let { e -> throw e }
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun action(fn: () -> Unit): TryFinallyChainScope {
|
||||||
|
contract {
|
||||||
|
callsInPlace(fn, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
safeAutoClose3e(
|
||||||
|
onCrossReturn = { throw Error("Cross return not allowed in tryFinallyChain{action{}}") },
|
||||||
|
onError = { err -> this._actualException?.addSuppressed(err) ?: run { this._actualException = err } },
|
||||||
|
action = fn
|
||||||
|
)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +1,101 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
@file:JvmName("SafeAutocloseKt")
|
||||||
|
|
||||||
package ru.landgrafhomyak.utility.highlevel_try_finally
|
package ru.landgrafhomyak.utility.highlevel_try_finally
|
||||||
|
|
||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
inline fun <R> safeAutoClose(onError: () -> Unit = {}, onSuccess: () -> Unit = {}, action: () -> R): R {
|
@Suppress("WRONG_INVOCATION_KIND")
|
||||||
|
inline fun <R> safeAutoClose1(
|
||||||
|
finally: () -> Unit,
|
||||||
|
action: () -> R
|
||||||
|
): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
|
||||||
|
callsInPlace(finally, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
return safeAutoClose3(onError = finally, onSuccess = finally, onCrossReturn = finally, action = action)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("WRONG_INVOCATION_KIND")
|
||||||
|
inline fun <R> safeAutoClose2(
|
||||||
|
onError: () -> Unit = {},
|
||||||
|
onSuccess: () -> Unit = {},
|
||||||
|
action: () -> R
|
||||||
|
): R {
|
||||||
contract {
|
contract {
|
||||||
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
|
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
|
||||||
callsInPlace(onError, InvocationKind.AT_MOST_ONCE)
|
callsInPlace(onError, InvocationKind.AT_MOST_ONCE)
|
||||||
callsInPlace(onSuccess, InvocationKind.EXACTLY_ONCE)
|
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
|
||||||
}
|
}
|
||||||
|
return safeAutoClose3(onError = onError, onSuccess = onSuccess, onCrossReturn = onSuccess, action = action)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("WRONG_INVOCATION_KIND")
|
||||||
|
inline fun <R> safeAutoClose2e(
|
||||||
|
onError: (Throwable) -> Unit = { _ -> },
|
||||||
|
onSuccess: () -> Unit = {},
|
||||||
|
action: () -> R
|
||||||
|
): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
|
||||||
|
callsInPlace(onError, InvocationKind.AT_MOST_ONCE)
|
||||||
|
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
|
||||||
|
}
|
||||||
|
return safeAutoClose3e(onError = onError, onSuccess = onSuccess, onCrossReturn = onSuccess, action = action)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <R> safeAutoClose3(
|
||||||
|
onError: () -> Unit = {},
|
||||||
|
onSuccess: () -> Unit = {},
|
||||||
|
onCrossReturn: () -> Unit = {},
|
||||||
|
action: () -> R
|
||||||
|
): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
|
||||||
|
callsInPlace(onError, InvocationKind.AT_MOST_ONCE)
|
||||||
|
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
|
||||||
|
callsInPlace(onCrossReturn, InvocationKind.AT_MOST_ONCE)
|
||||||
|
}
|
||||||
|
return safeAutoClose3e(onError = { t -> onError() }, onSuccess = onSuccess, onCrossReturn = onCrossReturn, action = action)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <R> safeAutoClose3e(
|
||||||
|
onError: (Throwable) -> Unit = { _ -> },
|
||||||
|
onSuccess: () -> Unit = {},
|
||||||
|
onCrossReturn: () -> Unit = {},
|
||||||
|
action: () -> R
|
||||||
|
): R {
|
||||||
|
contract {
|
||||||
|
callsInPlace(action, InvocationKind.EXACTLY_ONCE)
|
||||||
|
callsInPlace(onError, InvocationKind.AT_MOST_ONCE)
|
||||||
|
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
|
||||||
|
callsInPlace(onCrossReturn, InvocationKind.AT_MOST_ONCE)
|
||||||
|
}
|
||||||
|
|
||||||
val ret: R
|
val ret: R
|
||||||
var wasError = false
|
var wasError = false
|
||||||
|
var crossReturned = true
|
||||||
try {
|
try {
|
||||||
ret = action()
|
ret = action()
|
||||||
|
crossReturned = false
|
||||||
} catch (e1: Throwable) {
|
} catch (e1: Throwable) {
|
||||||
wasError = true
|
wasError = true
|
||||||
try {
|
try {
|
||||||
onError()
|
onError(e1)
|
||||||
} catch (e2: Throwable) {
|
} catch (e2: Throwable) {
|
||||||
e1.addSuppressed(e2)
|
ExceptionsKt.addSuppressed(e1, e2)
|
||||||
}
|
}
|
||||||
throw e1
|
throw e1
|
||||||
} finally {
|
} finally {
|
||||||
if (!wasError)
|
if (!wasError) {
|
||||||
|
if (crossReturned)
|
||||||
|
onCrossReturn()
|
||||||
|
else
|
||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
@file:JvmName("TryFinallyChainKt")
|
||||||
|
|
||||||
|
package ru.landgrafhomyak.utility.highlevel_try_finally
|
||||||
|
|
||||||
|
import kotlin.contracts.InvocationKind
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
import kotlin.jvm.JvmName
|
||||||
|
|
||||||
|
inline fun tryFinallyChain(chains: (TryFinallyChainScope) -> Unit) {
|
||||||
|
contract {
|
||||||
|
callsInPlace(chains, InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
|
||||||
|
val scope = TryFinallyChainScope()
|
||||||
|
safeAutoClose3e(
|
||||||
|
onCrossReturn = { throw Error("Cross return not allowed in tryFinallyChain{}") },
|
||||||
|
onError = { err -> throw Error("Unexpected exception in tryFinallyChain{}; calling anything outside action{} block isn't allowed", err) },
|
||||||
|
onSuccess = { scope._throw() },
|
||||||
|
action = { chains(scope) }
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package ru.landgrafhomyak.utility.highlevel_try_finally
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal actual object ExceptionsKt {
|
||||||
|
@PublishedApi
|
||||||
|
@JvmStatic
|
||||||
|
internal actual fun addSuppressed(e1: Throwable?, e2: Throwable?) {
|
||||||
|
if (e1 == null) throw NullPointerException("e1")
|
||||||
|
if (e2 == null) throw NullPointerException("e2")
|
||||||
|
(java.lang.Throwable::addSuppressed)(e1 as java.lang.Throwable, e2)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
package ru.landgrafhomyak.utility.highlevel_try_finally.tests
|
||||||
|
|
||||||
|
import org.testng.annotations.Test
|
||||||
|
import ru.landgrafhomyak.utility.highlevel_try_finally.safeAutoClose1
|
||||||
|
|
||||||
|
@Test
|
||||||
|
class KotlinStdlibDependencyTest {
|
||||||
|
@Test
|
||||||
|
fun testNoKotlinStdlib() {
|
||||||
|
try {
|
||||||
|
if (KotlinVersion.CURRENT.major != -1)
|
||||||
|
throw AssertionError("Kotlin stdlib still in runtime classpath")
|
||||||
|
} catch (_: LinkageError) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CustomTestException : RuntimeException()
|
||||||
|
private object CustomUnit
|
||||||
|
|
||||||
|
private fun throw7throwFn() {
|
||||||
|
safeAutoClose1(finally = { throw CustomTestException() }, action = { throw CustomTestException() })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline fun throw7throwIn() {
|
||||||
|
safeAutoClose1(finally = { throw CustomTestException() }, action = { throw CustomTestException() })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(dependsOnMethods = ["testNoKotlinStdlib"])
|
||||||
|
fun testAutoCloseFn() {
|
||||||
|
try {
|
||||||
|
throw7throwFn()
|
||||||
|
} catch (_: CustomTestException) {
|
||||||
|
} catch (le: LinkageError) {
|
||||||
|
throw AssertionError("safeAutoClose still has dependency on kotlin stdlib", le)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(dependsOnMethods = ["testNoKotlinStdlib"])
|
||||||
|
fun testAutoCloseIn() {
|
||||||
|
try {
|
||||||
|
throw7throwIn()
|
||||||
|
} catch (_: CustomTestException) {
|
||||||
|
} catch (le: LinkageError) {
|
||||||
|
throw AssertionError("safeAutoClose still has dependency on kotlin stdlib", le)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package ru.landgrafhomyak.utility.highlevel_try_finally
|
||||||
|
|
||||||
|
import kotlin.addSuppressed as kotlinAddSuppressed
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal actual object ExceptionsKt {
|
||||||
|
@PublishedApi
|
||||||
|
internal actual fun addSuppressed(e1: Throwable?, e2: Throwable?) {
|
||||||
|
e1!!.kotlinAddSuppressed(e2!!)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user