Skip to content

Commit

Permalink
fix: IllegalArgumentException: Failed requirement when attempting t…
Browse files Browse the repository at this point in the history
…o copy an annotation with an argument of type `KClass`
  • Loading branch information
ForteScarlet committed Jul 3, 2024
1 parent 964217d commit 6586751
Show file tree
Hide file tree
Showing 15 changed files with 376 additions and 49 deletions.
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/IProject.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object IProject : ProjectDetail() {

// Remember the libs.versions.toml!
val ktVersion = "2.0.20-Beta1"
val pluginVersion = "0.9.1"
val pluginVersion = "0.9.2"

override val version: String = "$ktVersion-$pluginVersion"

Expand Down Expand Up @@ -46,6 +46,6 @@ object IProject : ProjectDetail() {
fun Project.setupWith(ktVersion: String) {
group = IProject.GROUP
description = IProject.DESCRIPTION
val mergedVersion = ktVersion + "-" + IProject.pluginVersion.toString()
val mergedVersion = ktVersion + "-" + IProject.pluginVersion
version = if (IS_SNAPSHOT) "$mergedVersion-SNAPSHOT" else mergedVersion
}
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,29 @@ class SuspendTransformFirTransformer(
excludes.any { ex -> annotationClassId == ex }
}

addAll(notCompileAnnotationsCopied)
/*
* Create a new annotation based the annotation from the original function.
* It will be crashed with `IllegalArgumentException: Failed requirement`
* when using the `notCompileAnnotationsCopied` directly
* if there have some arguments with type `KClass`,
* e.g. `annotation class OneAnnotation(val target: KClass<*>)` or `kotlin.OptIn`.
*
* See https://github.com/ForteScarlet/kotlin-suspend-transform-compiler-plugin/issues/56
*/
val copied = notCompileAnnotationsCopied.map { a ->
buildAnnotation {
annotationTypeRef = buildResolvedTypeRef {
type = a.resolvedType
}
this.typeArguments.addAll(a.typeArguments)
this.argumentMapping = buildAnnotationArgumentMapping {
this.source = a.source
this.mapping.putAll(a.argumentMapping.mapping)
}
}
}

addAll(copied)
}

// try add @Generated(by = ...)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,10 @@ public void testOverride() {
public void testTypeAttr() {
runTest("src/testData/codegen/typeAttr.kt");
}

@Test
@TestMetadata("opt.kt")
public void testOpt() {
runTest("src/testData/codegen/opt.kt");
}
}
53 changes: 53 additions & 0 deletions compiler/suspend-transform-plugin/src/testData/codegen/opt.asm.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
public abstract interface OneOptAnno : java/lang/Object, java/lang/annotation/Annotation {

}

final class OptInTest$runAsync$1 : kotlin/coroutines/jvm/internal/SuspendLambda, kotlin/jvm/functions/Function1 {
int label

final OptInTest this$0

void <init>(OptInTest $receiver, kotlin.coroutines.Continuation $completion)

public final kotlin.coroutines.Continuation create(kotlin.coroutines.Continuation $completion)

public final java.lang.Object invoke(kotlin.coroutines.Continuation p1)

public java.lang.Object invoke(java.lang.Object p1)

public final java.lang.Object invokeSuspend(java.lang.Object $result)
}

final class OptInTest$runBlocking$1 : kotlin/coroutines/jvm/internal/SuspendLambda, kotlin/jvm/functions/Function1 {
int label

final OptInTest this$0

void <init>(OptInTest $receiver, kotlin.coroutines.Continuation $completion)

public final kotlin.coroutines.Continuation create(kotlin.coroutines.Continuation $completion)

public final java.lang.Object invoke(kotlin.coroutines.Continuation p1)

public java.lang.Object invoke(java.lang.Object p1)

public final java.lang.Object invokeSuspend(java.lang.Object $result)
}

public final class OptInTest : java/lang/Object {
public void <init>()

public final static java.lang.Object access$run0(OptInTest $this, kotlin.coroutines.Continuation $completion)

public final java.lang.Object run(kotlin.coroutines.Continuation $completion)

private final java.lang.Object run0(kotlin.coroutines.Continuation $completion)

public final java.util.concurrent.CompletableFuture runAsync()

public final int runBlocking()
}

public abstract interface Values : java/lang/Object, java/lang/annotation/Annotation {
public abstract java.lang.Class target()
}
125 changes: 125 additions & 0 deletions compiler/suspend-transform-plugin/src/testData/codegen/opt.fir.ir.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
FILE fqName:<root> fileName:/Main.kt
CLASS ANNOTATION_CLASS name:OneOptAnno modality:OPEN visibility:public superTypes:[kotlin.Annotation]
annotations:
RequiresOptIn(message = <null>, level = GET_ENUM 'ENUM_ENTRY IR_EXTERNAL_DECLARATION_STUB name:ERROR' type=kotlin.RequiresOptIn.Level)
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.OneOptAnno
CONSTRUCTOR visibility:public <> () returnType:<root>.OneOptAnno [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in kotlin.Any'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS ANNOTATION_CLASS name:OneOptAnno modality:OPEN visibility:public superTypes:[kotlin.Annotation]'
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in kotlin.Annotation
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Annotation
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in kotlin.Annotation
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS ANNOTATION_CLASS name:Values modality:OPEN visibility:public superTypes:[kotlin.Annotation]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.Values
PROPERTY name:target visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:target type:kotlin.reflect.KClass<*> visibility:private [final]
EXPRESSION_BODY
GET_VAR 'target: kotlin.reflect.KClass<*> declared in <root>.Values.<init>' type=kotlin.reflect.KClass<*> origin=INITIALIZE_PROPERTY_FROM_PARAMETER
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-target> visibility:public modality:FINAL <> ($this:<root>.Values) returnType:kotlin.reflect.KClass<*>
correspondingProperty: PROPERTY name:target visibility:public modality:FINAL [val]
$this: VALUE_PARAMETER name:<this> type:<root>.Values
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-target> (): kotlin.reflect.KClass<*> declared in <root>.Values'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:target type:kotlin.reflect.KClass<*> visibility:private [final]' type=kotlin.reflect.KClass<*> origin=null
receiver: GET_VAR '<this>: <root>.Values declared in <root>.Values.<get-target>' type=<root>.Values origin=null
CONSTRUCTOR visibility:public <> (target:kotlin.reflect.KClass<*>) returnType:<root>.Values [primary]
VALUE_PARAMETER name:target index:0 type:kotlin.reflect.KClass<*>
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in kotlin.Any'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS ANNOTATION_CLASS name:Values modality:OPEN visibility:public superTypes:[kotlin.Annotation]'
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in kotlin.Annotation
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Annotation
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in kotlin.Annotation
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS CLASS name:OptInTest modality:FINAL visibility:public superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.OptInTest
CONSTRUCTOR visibility:public <> () returnType:<root>.OptInTest [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in kotlin.Any'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:OptInTest modality:FINAL visibility:public superTypes:[kotlin.Any]'
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN GENERATED[love.forte.plugin.suspendtrans.fir.SuspendTransformPluginKey] name:runAsync visibility:public modality:FINAL <> ($this:<root>.OptInTest) returnType:java.util.concurrent.CompletableFuture
annotations:
OptIn(markerClass = [CLASS_REFERENCE 'CLASS ANNOTATION_CLASS name:OneOptAnno modality:OPEN visibility:public superTypes:[kotlin.Annotation]' type=kotlin.reflect.KClass<<root>.OneOptAnno>])
Values(target = CLASS_REFERENCE 'CLASS CLASS name:OptInTest modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.reflect.KClass<<root>.OptInTest>)
JvmBlocking(baseName = <null>, suffix = <null>, asProperty = <null>)
Api4J
$this: VALUE_PARAMETER name:<this> type:<root>.OptInTest
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun runAsync (): java.util.concurrent.CompletableFuture declared in <root>.OptInTest'
CALL 'public final fun $runInAsync$ <T> (block: kotlin.coroutines.SuspendFunction0<T of love.forte.plugin.suspendtrans.runtime.$runInAsync$>, scope: kotlinx.coroutines.CoroutineScope?): java.util.concurrent.CompletableFuture declared in love.forte.plugin.suspendtrans.runtime' type=java.util.concurrent.CompletableFuture origin=null
<T>: <none>
block: FUN_EXPR type=kotlin.coroutines.SuspendFunction0<java.util.concurrent.CompletableFuture> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<no name provided> visibility:local modality:FINAL <> () returnType:java.util.concurrent.CompletableFuture [suspend]
BLOCK_BODY
RETURN type=kotlin.Nothing from='local final fun <no name provided> (): java.util.concurrent.CompletableFuture declared in <root>.OptInTest.runAsync'
CALL 'public final fun run (): kotlin.Int declared in <root>.OptInTest' type=kotlin.Int origin=null
$this: GET_VAR '<this>: <root>.OptInTest declared in <root>.OptInTest.runAsync' type=<root>.OptInTest origin=null
FUN GENERATED[love.forte.plugin.suspendtrans.fir.SuspendTransformPluginKey] name:runBlocking visibility:public modality:FINAL <> ($this:<root>.OptInTest) returnType:kotlin.Int
annotations:
OptIn(markerClass = [CLASS_REFERENCE 'CLASS ANNOTATION_CLASS name:OneOptAnno modality:OPEN visibility:public superTypes:[kotlin.Annotation]' type=kotlin.reflect.KClass<<root>.OneOptAnno>])
Values(target = CLASS_REFERENCE 'CLASS CLASS name:OptInTest modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.reflect.KClass<<root>.OptInTest>)
JvmAsync(baseName = <null>, suffix = <null>, asProperty = <null>)
Api4J
$this: VALUE_PARAMETER name:<this> type:<root>.OptInTest
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun runBlocking (): kotlin.Int declared in <root>.OptInTest'
CALL 'public final fun $runInBlocking$ <T> (block: kotlin.coroutines.SuspendFunction0<T of love.forte.plugin.suspendtrans.runtime.$runInBlocking$>): T of love.forte.plugin.suspendtrans.runtime.$runInBlocking$ declared in love.forte.plugin.suspendtrans.runtime' type=T of love.forte.plugin.suspendtrans.runtime.$runInBlocking$ origin=null
<T>: <none>
block: FUN_EXPR type=kotlin.coroutines.SuspendFunction0<kotlin.Int> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<no name provided> visibility:local modality:FINAL <> () returnType:kotlin.Int [suspend]
BLOCK_BODY
RETURN type=kotlin.Nothing from='local final fun <no name provided> (): kotlin.Int declared in <root>.OptInTest.runBlocking'
CALL 'public final fun run (): kotlin.Int declared in <root>.OptInTest' type=kotlin.Int origin=null
$this: GET_VAR '<this>: <root>.OptInTest declared in <root>.OptInTest.runBlocking' type=<root>.OptInTest origin=null
FUN name:run visibility:public modality:FINAL <> ($this:<root>.OptInTest) returnType:kotlin.Int [suspend]
annotations:
OptIn(markerClass = [CLASS_REFERENCE 'CLASS ANNOTATION_CLASS name:OneOptAnno modality:OPEN visibility:public superTypes:[kotlin.Annotation]' type=kotlin.reflect.KClass<<root>.OneOptAnno>])
Values(target = CLASS_REFERENCE 'CLASS CLASS name:OptInTest modality:FINAL visibility:public superTypes:[kotlin.Any]' type=kotlin.reflect.KClass<<root>.OptInTest>)
JvmBlocking(baseName = <null>, suffix = <null>, asProperty = <null>)
JvmAsync(baseName = <null>, suffix = <null>, asProperty = <null>)
JvmSynthetic
$this: VALUE_PARAMETER name:<this> type:<root>.OptInTest
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun run (): kotlin.Int declared in <root>.OptInTest'
CALL 'private final fun run0 (): kotlin.Int declared in <root>.OptInTest' type=kotlin.Int origin=null
$this: GET_VAR '<this>: <root>.OptInTest declared in <root>.OptInTest.run' type=<root>.OptInTest origin=null
FUN name:run0 visibility:private modality:FINAL <> ($this:<root>.OptInTest) returnType:kotlin.Int [suspend]
annotations:
OneOptAnno
$this: VALUE_PARAMETER name:<this> type:<root>.OptInTest
BLOCK_BODY
RETURN type=kotlin.Nothing from='private final fun run0 (): kotlin.Int declared in <root>.OptInTest'
CONST Int type=kotlin.Int value=1
34 changes: 34 additions & 0 deletions compiler/suspend-transform-plugin/src/testData/codegen/opt.fir.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
FILE: Main.kt
@R|kotlin/RequiresOptIn|(level = Q|kotlin/RequiresOptIn.Level|.R|kotlin/RequiresOptIn.Level.ERROR|) public final annotation class OneOptAnno : R|kotlin/Annotation| {
public constructor(): R|OneOptAnno| {
super<R|kotlin/Any|>()
}

}
public final annotation class Values : R|kotlin/Annotation| {
public constructor(target: R|kotlin/reflect/KClass<*>|): R|Values| {
super<R|kotlin/Any|>()
}

public final val target: R|kotlin/reflect/KClass<*>| = R|<local>/target|
public get(): R|kotlin/reflect/KClass<*>|

}
public final class OptInTest : R|kotlin/Any| {
public constructor(): R|OptInTest| {
super<R|kotlin/Any|>()
}

@R|OneOptAnno|() private final suspend fun run0(): R|kotlin/Int| {
^run0 Int(1)
}

@R|kotlin/OptIn|(markerClass = vararg(<getClass>(Q|OneOptAnno|))) @R|Values|(target = <getClass>(Q|OptInTest|)) @R|love/forte/plugin/suspendtrans/annotation/JvmBlocking|() @R|love/forte/plugin/suspendtrans/annotation/JvmAsync|() public final suspend fun run(): R|kotlin/Int| {
^run this@R|/OptInTest|.R|/OptInTest.run0|()
}

@R|kotlin/OptIn|(markerClass = vararg(<getClass>(Q|OneOptAnno|))) @R|Values|(target = <getClass>(Q|OptInTest|)) @R|love/forte/plugin/suspendtrans/annotation/JvmBlocking|() @R|love/forte/plugin/suspendtrans/annotation/Api4J|() public final fun runAsync(): R|java/util/concurrent/CompletableFuture<out kotlin/Int>|

@R|kotlin/OptIn|(markerClass = vararg(<getClass>(Q|OneOptAnno|))) @R|Values|(target = <getClass>(Q|OptInTest|)) @R|love/forte/plugin/suspendtrans/annotation/JvmAsync|() @R|love/forte/plugin/suspendtrans/annotation/Api4J|() public final fun runBlocking(): R|kotlin/Int|

}
27 changes: 27 additions & 0 deletions compiler/suspend-transform-plugin/src/testData/codegen/opt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// FIR_DUMP
// DUMP_IR
// SOURCE
// FILE: Main.kt [MainKt#main]

import kotlinx.coroutines.suspendCancellableCoroutine
import love.forte.plugin.suspendtrans.annotation.JvmAsync
import love.forte.plugin.suspendtrans.annotation.JvmBlocking
import kotlin.annotation.AnnotationRetention.SOURCE
import kotlin.coroutines.resume
import kotlin.reflect.KClass

@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
annotation class OneOptAnno

annotation class Values(val target: KClass<*>)

class OptInTest {
@OneOptAnno
private suspend fun run0() = 1

@OptIn(OneOptAnno::class)
@Values(OptInTest::class)
@JvmBlocking
@JvmAsync
suspend fun run(): Int = run0()
}
31 changes: 8 additions & 23 deletions samples/sample-js/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,55 +25,40 @@ repositories {

apply(plugin = "love.forte.plugin.suspend-transform")

//tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
// kotlinOptions {
// useK2
// languageVersion = "2.0"
// }
//}



@OptIn(ExperimentalKotlinGradlePluginApi::class)
kotlin {
compilerOptions {
freeCompilerArgs.addAll(
"-Xexpect-actual-classes"
)
}

js(IR) {
nodejs()
useEsModules()
generateTypeScriptDefinitions()
binaries.executable()

@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
target = "es2015"
useEsClasses = true
freeCompilerArgs.addAll(
// https://kotlinlang.org/docs/whatsnew20.html#per-file-compilation-for-kotlin-js-projects
"-Xir-per-file"
"-Xir-per-file",
)
}

// compilations.all {
// kotlinOptions {
// useEsClasses = true
// }
// }
}

sourceSets {
named("jsMain") {
dependencies {
implementation(kotlin("stdlib"))
// val pluginVersion = "0.4.0"
// api("love.forte.plugin.suspend-transform:suspend-transform-runtime:$pluginVersion")
// api("love.forte.plugin.suspend-transform:suspend-transform-annotation:$pluginVersion")
// implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
api(libs.kotlinx.coroutines.core)
}
}
}
}



extensions.getByType<SuspendTransformGradleExtension>().apply {
transformers[TargetPlatform.JS] = mutableListOf(
SuspendTransformConfiguration.jsPromiseTransformer.copy(
Expand Down
17 changes: 17 additions & 0 deletions samples/sample-js/src/commonMain/kotlin/Key.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@file:OptIn(ExperimentalJsExport::class)

import love.forte.plugin.suspendtrans.annotation.JsPromise


/**
*
* @author ForteScarlet
*/
@JsExport
abstract class Key {

@JsPromise
@JsExport.Ignore
abstract suspend fun run(): Any

}
10 changes: 10 additions & 0 deletions samples/sample-js/src/commonMain/kotlin/KeyImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import love.forte.plugin.suspendtrans.annotation.JsPromise

/**
*
* @author ForteScarlet
*/
expect class KeyImpl : Key {
@JsPromise
override suspend fun run(): ByteArray
}
Loading

0 comments on commit 6586751

Please sign in to comment.