How do I prematurely end a script processing? I'm ...
# scripting
v
How do I prematurely end a script processing? I'm inside a Gradle Kotlin DSL script and want to stop processing. I simply tried
return
but it is a syntax violation. ๐Ÿ˜•
m
System.exit(1) ? ๐Ÿ™ˆ
Not sure how much Gradle would like that...
v
No too much probably o_O
m
What is "stop processing" in the context of Gradle?
You can "stop a task" but I'm not sure you can stop Gradle from the build scripts ?
v
Nah, you misunderstand. I want neither stop a Task nor Gradle
It is basically a Kotlin Script from what I want to exit.
I have a pre-compiled script plugin that is written as Kotlin script
In there I want to have
Copy code
if (whatever) return
doMoreStuff()
If it were written as normal Kotlin class or Java Class I would be inside an
apply
function at that time where I could simply do
return
The only ways I came up were wrapping all in a function and calling it, but that is so JavaScript-ish or instead doing
Copy code
if (!whatever) {
    doMoreStuff()
}
but that increases the indentation for each such check which is why I usually have the other style
m
Yep, I see
Not sure why
return
is not allowed as a top level statement while others like
println
are
v
Well, let's see what JetBrains thinks: https://youtrack.jetbrains.com/issue/KT-44243
โญ 1
e
maybe a little better than a `fun dummy()`:
Copy code
run {
    if (condition) {
        return@run
    }
    doOtherStuff()
}
for what it's worth, you're not allowed to
return
from the top level of shell scripts, Python scripts, Perl scripts, Ruby scripts, ... anything else I can think of
v
Ah, forgot about
run
, thanks
Well, just that they don't allow it does not mean that Kotlin couldn't allow it. ๐Ÿ˜„ For shell scripts you have exit, don't you? Which is different from exiting the whole JVM. Dunno about the others
e
actually shell script is kind of a weird case because if your script is being sourced (via
.
), return at the "top" level is allowed - it returns to the source-r. but if your script is executed directly (via
sh
), return at the top level is not allowed
I think it makes some sense, there's nowhere to return to. what's wrong with System.exit()?
v
o_O
That it exits the JVM?
It should return to whoever started executing the script. Don't just think about main kts scripts. Think about JSR-223 scripts embedded in some software. Or like described above my use-case is a Gradle Kotlin DSL script. I just want to return from that and continue with the rest, not exiting the JVM
e
if it runs in another classloader, you can handle exit
dunno if that's the most elegant way of handling it, but it's not clear to me that
return
is better
v
It is to me, let's see how JetBrains thinks ๐Ÿ™‚ Especially such classloader hacks would be use-site, everyone embedding scripts would need to do this separately. A proper language-supported way would imho be better.
Oh, no, the
run
version does not work. ๐Ÿ˜ž
org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Couldn't inline method call 'run' into
public constructor Build_label_gradle(target: org.gradle.api.Project, `$$implicitReceiver0`: org.gradle.api.Project) defined in de.empic.build.Build_label_gradle
๐Ÿ˜ข 1
Copy code
fun run(block: () -> Unit) = block()
run {
    if (condition) {
        return@run
    }
    doOtherStuff()
}
This works, but makes it a bit uglier again
Hm, actually the
run
trick does work. It just didn't in my specific case. I had this failing
Copy code
run {
    if (true) return@run
    ByteArrayOutputStream().use { }
}
While these work:
Copy code
run {
    if (true) return@run
    ByteArrayOutputStream().use { }
    println()
}
Copy code
run {
    if (true) return@run
    ByteArrayOutputStream()
}
Copy code
also {
    if (true) return@also
    ByteArrayOutputStream().use { }
}
Copy code
apply {
    if (true) return@apply
    ByteArrayOutputStream().use { }
}
e
strange. almost as if it's inferring some private type for the return value of
.use { }
v
I even reproduced it now in an absolutely empty Gradle build only containing these lines
Copy code
e: D:\Sourcecode\other\showcase\build.gradle.kts:1:1: Back-end (JVM) Internal error: Couldn't inline method call 'run' into
public constructor Build_gradle(host: org.gradle.kotlin.dsl.support.KotlinScriptHost<org.gradle.api.Project>, `$$implicitReceiver0`: org.gradle.api.Project) defined in Build_gradle
run {
    if (true) return@run
    java.io.ByteArrayOutputStream().use { }
}

Cause: run (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;:
  @Lkotlin/internal/InlineOnly;() // invisible
   L0
    LDC 0
    ISTORE 2
   L1
    LINENUMBER 54 L1
    ICONST_0
    ISTORE 3
   L2
    LINENUMBER 57 L2
    ALOAD 1
    ALOAD 0
    INVOKEINTERFACE kotlin/jvm/functions/Function1.invoke (Ljava/lang/Object;)Ljava/lang/Object; (itf)
    ARETURN
   L3
    LOCALVARIABLE $this$run Ljava/lang/Object; L0 L3 0
    LOCALVARIABLE block Lkotlin/jvm/functions/Function1; L0 L3 1
    LOCALVARIABLE $i$f$run I L1 L3 2
    MAXSTACK = 2
    MAXLOCALS = 4

File being compiled: (1,1) in C:/Users/bkautler/AppData/Local/Temp/gradle-kotlin-dsl-10303106793239029513.tmp/build.gradle.kts
The root cause java.lang.UnsupportedOperationException was thrown at: org.jetbrains.kotlin.load.kotlin.TypeSignatureMappingKt.mapType(typeSignatureMapping.kt:95)
Can you estimate whether I should report this to JetBrains or Kotlin? At this stage I'd say it is pretty certainly a bug in either
e
I'd lean towards Kotlin but I'm not sure
v
Yeah, me too. Gonna open a ticket then we see what they say.
No, I'm not, Gradle 6.8 with Kotlin 1.4.20 to the rescue, there it works without problem ๐Ÿ™‚
๐Ÿ‘ 1