Compare commits

...

11 Commits
v0.3 ... master

11 changed files with 249 additions and 3 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
/.idea/
gradle/
.gradle/
build/
*.class
*.jar
/out/
/gradlew*
.kotlin/

101
build.gradle.kts Normal file
View 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
View 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
View File

@ -0,0 +1 @@
rootProject.name = "highlevel-try-finally"

View File

@ -0,0 +1,7 @@
package ru.landgrafhomyak.utility.highlevel_try_finally
@PublishedApi
internal expect object ExceptionsKt {
@PublishedApi
internal fun addSuppressed(e1: Throwable?, e2: Throwable?)
}

View File

@ -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
}
}

View File

@ -1,7 +1,11 @@
@file:Suppress("unused")
@file:JvmName("SafeAutocloseKt")
package ru.landgrafhomyak.utility.highlevel_try_finally
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmName
@Suppress("WRONG_INVOCATION_KIND")
inline fun <R> safeAutoClose1(
@ -43,7 +47,6 @@ inline fun <R> safeAutoClose2e(
return safeAutoClose3e(onError = onError, onSuccess = onSuccess, onCrossReturn = onSuccess, action = action)
}
inline fun <R> safeAutoClose3(
onError: () -> Unit = {},
onSuccess: () -> Unit = {},
@ -83,7 +86,7 @@ inline fun <R> safeAutoClose3e(
try {
onError(e1)
} catch (e2: Throwable) {
e1.addSuppressed(e2)
ExceptionsKt.addSuppressed(e1, e2)
}
throw e1
} finally {
@ -95,4 +98,4 @@ inline fun <R> safeAutoClose3e(
}
}
return ret
}
}

View File

@ -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) }
)
}

View File

@ -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)
}
}

View File

@ -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)
}
}
}

View File

@ -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!!)
}
}