Kotlin / kotlin-script-examples

Examples of Kotlin Scripts and usages of the Kotlin Scripting API

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

No Simple Example Exists

kolotyluk opened this issue · comments

All these examples are too complicated, brittle, and fragile. Can someone please give the most simple working example possible with only two files

  1. main.kt
  2. script.kts

When I try to run the simple example I get

: wrong number of arguments: 0 expected: 1: java.lang.IllegalArgumentException: wrong number of arguments: 0 expected: 1

If you want a really simple example, you can look at https://github.com/Kotlin/kotlin-script-examples/blob/master/jvm/main-kts/MainKts.md, but those are using the built-in script definition (... .main.kts).

I don't think it's possible to give a simpler example than "basic" because of the need to define a separate jar for the script definition (class that the kts file "extends").

Re "simple example", did you mean https://github.com/Kotlin/kotlin-script-examples/tree/master/jvm/simple-main-kts? What steps are you doing?

https://github.com/Kotlin/kotlin-script-examples/tree/master/jvm/basic/jvm-simple-script is what I was trying to follow, but I did not want to clone the repo, I just wanted a standalone environment to play with... This is what I tried, but could not get it to work.

Main.kt

import java.io.File
import kotlin.script.experimental.annotations.KotlinScript
import kotlin.script.experimental.api.EvaluationResult
import kotlin.script.experimental.api.ResultWithDiagnostics
import kotlin.script.experimental.api.ScriptDiagnostic
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvm.dependenciesFromCurrentContext
import kotlin.script.experimental.jvm.jvm

fun main(args: Array<String>) {
    println("Hello World!")

    // Try adding program arguments via Run/Debug configuration.
    // Learn more about running applications: https://www.jetbrains.com/help/idea/running-applications.html.
    println("Program arguments: ${args.joinToString()}")

    if (args.isEmpty()) {
        val file = File("default.kts")

        println(file.absolutePath)

        val results = evalFile(File("default.kts"))

        results.reports.forEach {
            if (it.severity > ScriptDiagnostic.Severity.DEBUG) {
                println(" : ${it.message}" + if (it.exception == null) "" else ": ${it.exception}")
            }
        }
    }


}

@KotlinScript(fileExtension = "simplescript.kts")
abstract class SimpleScript

fun evalFile(scriptFile: File): ResultWithDiagnostics<EvaluationResult> {
    val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<SimpleScript> {
        jvm {
            // configure dependencies for compilation, they should contain at least the script base class and
            // its dependencies
            // variant 1: try to extract current classpath and take only a path to the specified "script.jar"
//            dependenciesFromCurrentContext(
//                "script" /* script library jar name (exact or without a version) */
//            )
            // variant 2: try to extract current classpath and use it for the compilation without filtering
            dependenciesFromCurrentContext(wholeClasspath = true)
            // variant 3: try to extract a classpath from a particular classloader (or Thread.contextClassLoader by default)
            // filtering as in the variat 1 is supported too
//            dependenciesFromClassloader(classLoader = SimpleScript::class.java.classLoader, wholeClasspath = true)
            // variant 4: explicit classpath
//            updateClasspath(listOf(File("/path/to/jar")))
        }
    }

    return BasicJvmScriptingHost().eval(scriptFile.toScriptSource(), compilationConfiguration, null)
}

default.kts

println("hello from Kotlin")

When I run it I get

Hello World!
Program arguments: 
/Users/eric.kolotyluk/IdeaProjects/Scripts/default.kts
 : Using new faster version of JAR FS: it should make your build faster, but the new implementation is experimental
 : wrong number of arguments: 0 expected: 1: java.lang.IllegalArgumentException: wrong number of arguments: 0 expected: 1

Process finished with exit code 0

I reproduced your problem in: https://github.com/TWiStErRob/repros/tree/main/kotlin/basic-script-hello-world
you can see the steps here: https://github.com/TWiStErRob/repros/commits/main/kotlin/basic-script-hello-world

I recommend you don't hide the error message by doing this:

            if (it.severity > ScriptDiagnostic.Severity.DEBUG) {
                val ex = it.exception
                println(" : ${it.message}" + if (ex == null) "" else "\n${ex.stackTraceToString()}")
            }

it gives way more info:

java.lang.IllegalArgumentException: wrong number of arguments
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at kotlin.script.experimental.jvm.BasicJvmScriptEvaluator.evalWithConfigAndOtherScriptsResults(BasicJvmScriptEvaluator.kt:100)
        at kotlin.script.experimental.jvm.BasicJvmScriptEvaluator.invoke$suspendImpl(BasicJvmScriptEvaluator.kt:47)
        at kotlin.script.experimental.jvm.BasicJvmScriptEvaluator.invoke(BasicJvmScriptEvaluator.kt)
        at kotlin.script.experimental.host.BasicScriptingHost$eval$1.invokeSuspend(BasicScriptingHost.kt:48)
        at kotlin.script.experimental.host.BasicScriptingHost$eval$1.invoke(BasicScriptingHost.kt)
        at kotlin.script.experimental.host.BasicScriptingHost$eval$1.invoke(BasicScriptingHost.kt)
        at kotlin.script.experimental.host.BasicScriptingHost$runInCoroutineContext$1.invokeSuspend(BasicScriptingHost.kt:36)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlin.coroutines.ContinuationKt.startCoroutine(Continuation.kt:115)
        at kotlin.script.experimental.impl.RunSuspendKt.internalScriptingRunSuspend(runSuspend.kt:19)
        at kotlin.script.experimental.host.BasicScriptingHost.runInCoroutineContext(BasicScriptingHost.kt:36)
        at kotlin.script.experimental.host.BasicScriptingHost.eval(BasicScriptingHost.kt:46)
        at MainKt.evalFile(Main.kt:59)
        at MainKt.main(Main.kt:24)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.jetbrains.kotlin.runner.AbstractRunner.run(runners.kt:70)
        at org.jetbrains.kotlin.runner.Main.run(Main.kt:188)
        at org.jetbrains.kotlin.runner.Main.main(Main.kt:198)

at this point you can see the following:

  • it's trying to instantiate something from Constructor.newInstance
  • that something has a different number of parameters to the arguments passed in from wrong number of arguments
  • if you check the source code of the line that calls reflection you can see that it's trying to instantiate the script configuration, which I'm not sure what class is representing, this is where debugging would help on that line 100.
    A wild guess here is that the default from @KotlinScript is used, but those are objects so non-instantiatable.

Since there's a problem with the configuration, I made a wild guess, and found the solution:

rename your file to default.simplescript.kts and adjust your default File accordingly.

WOW... thanks so much... your sleuthing is beyond my Kotlin skills...

Anyway, it looks like simplescript comes from

val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<SimpleScript> {

So, mostly I just constructed this code by copy/paste from various sources, not really understanding what the code was doing. The original code has too many inter-dependencies on other things.

However, the end result is basically what I asked for in the first place: two simple files.

By comparison, I did something similar in Java, but using the runtime compiler, then loading the class file, and invoking it with reflection. The code is easier to read and understand, but it did take a lot of trial and error to get it right.

Years ago, I did something similar with Groovy, and it was a lot easier to load Groovy at runtime, and call it.

I also did something similar with Scala, but sort of cheated and just invoked Scala as scripts from the runtime by calling the Java Process API.

I like the idea of loading Kotlin source at runtime and executing it, but I hope it become much simpler.