ForteScarlet / kotlin-suspend-transform-compiler-plugin

A Kotlin compiler plugin for transforming suspend functions to platform-compatible non-suspend functions, such as the JVM Blocking API and CompletableFuture or JS Promise, etc. 🐱
MIT License
36 stars 3 forks source link

NoClassDefFoundError for IrFunctionExpressionImplKt #55

Closed Komdosh closed 5 months ago

Komdosh commented 5 months ago

Hello! Thank you for your library, it looks very helpful!

I have a problem with setup.

I have a multiplatform project (JVM + JS (Not wasm)). Kotlin version is 2.0.0. Am I missing something?

// build.gradle.kts of common:api
plugins {
    kotlin("multiplatform") apply true
    kotlin("plugin.serialization") apply true
    id("love.forte.plugin.suspend-transform") version "0.9.0"
}

kotlin {
    jvm {}

    js {
        browser { testTask { useMocha { this.timeout = "5s" } } }
    }

    sourceSets....
}

suspendTransform {
    useJsDefault()

    // I've tried with addJsTransformers also. (exclude jsIgnore)
}
// this file in commonMain (i've tried with files in jsMain - same error)
public class AuthClient(private val client: HttpClient) {

    @JsPromise
    public suspend fun config(): AuthConfig {
        return client.get("/config").body()
    }
}

I'm trying to compile :common:api:compileKotlinJs, but I'm getting an error:

> Task :common:api:compileKotlinJs FAILED
e: java.lang.NoClassDefFoundError: org/jetbrains/kotlin/ir/expressions/impl/IrFunctionExpressionImplKt
    at love.forte.plugin.suspendtrans.ir.SuspendTransformTransformerKt.generateTransformBodyForFunctionLambda(SuspendTransformTransformer.kt:360)
    at love.forte.plugin.suspendtrans.ir.SuspendTransformTransformerKt.access$generateTransformBodyForFunctionLambda(SuspendTransformTransformer.kt:1)
    at love.forte.plugin.suspendtrans.ir.SuspendTransformTransformer.resolveFunctionBodyByDescriptor(SuspendTransformTransformer.kt:491)
    at love.forte.plugin.suspendtrans.ir.SuspendTransformTransformer.resolveFunctionBodyByDescriptor$default(SuspendTransformTransformer.kt:59)
    at love.forte.plugin.suspendtrans.ir.SuspendTransformTransformer.visitFunctionNew(SuspendTransformTransformer.kt:46)
    at org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext.visitFunction(IrElementTransformerVoidWithContext.kt:83)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:128)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:131)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitSimpleFunction(IrElementTransformerVoid.kt:19)
    at org.jetbrains.kotlin.ir.declarations.IrSimpleFunction.accept(IrSimpleFunction.kt:36)
    at org.jetbrains.kotlin.ir.IrElementBase.transform(IrElementBase.kt:24)
    at org.jetbrains.kotlin.ir.util.TransformKt.transformInPlace(transform.kt:35)
    at org.jetbrains.kotlin.ir.declarations.IrClass.transformChildren(IrClass.kt:82)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitDeclaration(IrElementTransformerVoid.kt:40)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitClass(IrElementTransformerVoid.kt:54)
    at org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext.visitClassNew(IrElementTransformerVoidWithContext.kt:126)
    at org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext.visitClass(IrElementTransformerVoidWithContext.kt:62)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitClass(IrElementTransformerVoid.kt:57)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitClass(IrElementTransformerVoid.kt:19)
    at org.jetbrains.kotlin.ir.declarations.IrClass.accept(IrClass.kt:72)
    at org.jetbrains.kotlin.ir.IrElementBase.transform(IrElementBase.kt:24)
    at org.jetbrains.kotlin.ir.util.TransformKt.transformInPlace(transform.kt:35)
    at org.jetbrains.kotlin.ir.declarations.IrFile.transformChildren(IrFile.kt:38)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitPackageFragment(IrElementTransformerVoid.kt:146)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitFile(IrElementTransformerVoid.kt:160)
    at org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext.visitFileNew(IrElementTransformerVoidWithContext.kt:122)
    at org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext.visitFile(IrElementTransformerVoidWithContext.kt:55)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitFile(IrElementTransformerVoid.kt:163)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid.visitFile(IrElementTransformerVoid.kt:19)
    at org.jetbrains.kotlin.ir.declarations.IrFile.accept(IrFile.kt:28)
    at org.jetbrains.kotlin.ir.declarations.IrFile.transform(IrFile.kt:31)
    at org.jetbrains.kotlin.ir.declarations.IrFile.transform(IrFile.kt:20)
    at org.jetbrains.kotlin.ir.util.TransformKt.transformInPlace(transform.kt:35)
    at org.jetbrains.kotlin.ir.declarations.IrModuleFragment.transformChildren(IrModuleFragment.kt:43)
    at org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoidKt.transformChildrenVoid(IrElementTransformerVoid.kt:565)
    at love.forte.plugin.suspendtrans.ir.SuspendTransformIrGenerationExtension.generate(SuspendTransformIrGenerationExtension.kt:17)
    at org.jetbrains.kotlin.fir.pipeline.ConvertToIrKt.applyIrGenerationExtensions(convertToIr.kt:395)
    at org.jetbrains.kotlin.fir.pipeline.Fir2IrPipeline.runActualizationPipeline(convertToIr.kt:234)
    at org.jetbrains.kotlin.fir.pipeline.Fir2IrPipeline.convertToIrAndActualize(convertToIr.kt:133)
    at org.jetbrains.kotlin.fir.pipeline.ConvertToIrKt.convertToIrAndActualize(convertToIr.kt:102)
    at org.jetbrains.kotlin.cli.js.klib.JsCompilerPipelineKt.transformFirToIr(jsCompilerPipeline.kt:234)
    at org.jetbrains.kotlin.cli.js.K2JsIrCompiler.processSourceModuleWithK2(K2JsIrCompiler.kt:620)
    at org.jetbrains.kotlin.cli.js.K2JsIrCompiler.produceSourceModule(K2JsIrCompiler.kt:487)
    at org.jetbrains.kotlin.cli.js.K2JsIrCompiler.doExecute(K2JsIrCompiler.kt:327)
    at org.jetbrains.kotlin.cli.js.K2JSCompiler.doExecute(K2JSCompiler.java:70)
    at org.jetbrains.kotlin.cli.js.K2JSCompiler.doExecute(K2JSCompiler.java:33)
    at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:106)
    at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:50)
    at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
    at org.jetbrains.kotlin.incremental.IncrementalJsCompilerRunner.runCompiler(IncrementalJsCompilerRunner.kt:211)
    at org.jetbrains.kotlin.incremental.IncrementalJsCompilerRunner.runCompiler(IncrementalJsCompilerRunner.kt:86)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129)
    at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile$default(IncrementalCompilerRunner.kt:113)
    at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execJsIncrementalCompiler(CompileServiceImpl.kt:609)
    at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execJsIncrementalCompiler(CompileServiceImpl.kt:92)
    at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1759)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:360)
    at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
    at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:714)
    at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
    at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:598)
    at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:844)
    at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:721)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
    at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:720)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.ClassNotFoundException: org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImplKt
    at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
    ... 74 more

There is also a gradle hint for configuration:

> Configure project :common:api
w: A compileOnly dependency is used in targets: Kotlin/JS.
Dependencies:
    - love.forte.plugin.suspend-transform:suspend-transform-annotation:0.9.0 (source sets: jsMain)

Using compileOnly dependencies in these targets is not currently supported, because compileOnly dependencies must be present during the compilation of projects that depend on this project.

To ensure consistent compilation behaviour, compileOnly dependencies should be exposed as api dependencies.

Example:

    kotlin {
        sourceSets {
            nativeMain {
                dependencies {
                    compileOnly("org.example:lib:1.2.3")
                    // additionally add the compileOnly dependency as an api dependency:
                    api("org.example:lib:1.2.3")
                }
            }
        }
    }

This warning can be suppressed in gradle.properties:

    kotlin.suppressGradlePluginWarnings=IncorrectCompileOnlyDependencyWarning
ForteScarlet commented 5 months ago

Hi~ Based on your description, I created a project that I used to try to reproduce it, but unfortunately I don't seem to be reproducing it successfully. Here's its configuration:

plugins {
    kotlin("multiplatform") version "2.0.0"
    kotlin("plugin.serialization") version "2.0.0"
    id("love.forte.plugin.suspend-transform") version "0.9.0"
}

group = "plugin.demo"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

kotlin {
    jvm {}

    js {
        browser { testTask { useMocha { this.timeout = "5s" } } }
    }

    sourceSets.commonMain.dependencies {
        implementation("io.ktor:ktor-client-core:2.3.12")
    }
}

suspendTransform {
    useJsDefault()
}

This is the structure of the project: image

The AuthClient in the commonMain:

class AuthClient(private val client: HttpClient) {

    @JsPromise
    suspend fun config(): AuthConfig {
        return client.get("/config").body()
    }
}

class AuthConfig

fun main() {
    println(AuthClient(HttpClient()))
}

I executed compileKotlinJs and got a successful result and got the compiled content in build/classes/js/main/default/linkdata/root_package/0_.knm as follows:

public fun main(): kotlin.Unit { /* compiled code */ }

public final class AuthClient public constructor(client: io.ktor.client.HttpClient) {
    private final val client: io.ktor.client.HttpClient /* compiled code */

    @love.forte.plugin.suspendtrans.annotation.JsPromise public final suspend fun config(): AuthConfig { /* compiled code */ }

    @love.forte.plugin.suspendtrans.annotation.Api4Js public final fun configAsync(): kotlin.js.Promise<out AuthConfig> { /* compiled code */ }
}

public final class AuthConfig public constructor() {
}

🤔This seems like everything is in order, additional information about this project:

However, NoClassDefFoundError exceptions are usually caused by a Kotlin version mismatch. If you want to use the 0.9.0 version, Kotlin should be 2.0.0, so maybe that's a reason?

Komdosh commented 5 months ago

Oh, I forgot that i've changed the kotlin version to 2.0.20-Beta1. With 2.0.0 it works properly, thank you.