Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No Simple Example Exists #21

Open
kolotyluk opened this issue Nov 2, 2022 · 4 comments
Open

No Simple Example Exists #21

kolotyluk opened this issue Nov 2, 2022 · 4 comments

Comments

@kolotyluk
Copy link

kolotyluk commented Nov 2, 2022

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

@TWiStErRob
Copy link

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?

@kolotyluk
Copy link
Author

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

@TWiStErRob
Copy link

TWiStErRob commented Nov 3, 2022

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.

TWiStErRob added a commit to TWiStErRob/repros that referenced this issue Nov 3, 2022
TWiStErRob added a commit to TWiStErRob/repros that referenced this issue Nov 3, 2022
TWiStErRob added a commit to TWiStErRob/repros that referenced this issue Nov 3, 2022
@kolotyluk
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants