From eadeffbf878c039474b67d8d64d4c86f42de293f Mon Sep 17 00:00:00 2001 From: Forte Date: Thu, 3 Jul 2025 11:35:11 +0800 Subject: [PATCH 01/10] feat: try to support generic in annotation and some tests --- .../fir/SuspendTransformFirTransformer.kt | 37 ++++++++----- .../runners/CodeGenTestRunnerGenerated.java | 6 +++ ...spendTransformerEnvironmentConfigurator.kt | 53 +++++++++++++++++-- .../src/testData/codegen/typedAnno.fir.txt | 30 +++++++++++ .../src/testData/codegen/typedAnno.kt | 18 +++++++ .../annotation/JvmBlockingWithType.kt | 13 +++++ .../annotation/TypedJvmBlocking.jvm.kt | 12 +++++ 7 files changed, 154 insertions(+), 15 deletions(-) create mode 100644 compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.fir.txt create mode 100644 compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt create mode 100644 runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt create mode 100644 runtime/suspend-transform-annotation/src/jvmMain/kotlin/love.forte.plugin.suspendtrans/annotation/TypedJvmBlocking.jvm.kt diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt index 621823a4..4cdc183b 100644 --- a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt @@ -98,6 +98,7 @@ class SuspendTransformFirTransformer( val annotationData: TransformAnnotationData, val transformer: Transformer, val transformerFunctionSymbol: FirNamedFunctionSymbol, + val markAnnotationTypeArgument: FirTypeProjection?, ) private fun initScopeSymbol() { @@ -346,8 +347,9 @@ class SuspendTransformFirTransformer( thisValueParameters: List, bridgeFunSymbol: FirNamedFunctionSymbol, newFunTarget: FirFunctionTarget, - transformer: Transformer + funData: SyntheticFunData ): FirBlock = buildBlock { + val transformer = funData.transformer this.source = originFunc.body?.source // lambda: suspend () -> T @@ -434,7 +436,7 @@ class SuspendTransformFirTransformer( } lambdaTarget.bind(lambda) - val returnType = resolveReturnType(transformer, originFunc.returnTypeRef) + val returnType = resolveReturnType(funData, originFunc.returnTypeRef) this.statements.add( buildReturnExpression { @@ -640,7 +642,7 @@ class SuspendTransformFirTransformer( copyParameters() // resolve returnType (with wrapped) after copyParameters - returnTypeRef = resolveReturnType(funData.transformer, returnTypeRef) + returnTypeRef = resolveReturnType(funData, returnTypeRef) val thisReceiverParameter = this.receiverParameter val thisContextParameters = this.contextParameters @@ -659,7 +661,7 @@ class SuspendTransformFirTransformer( thisValueParameters, realBridgeFunSymbol, newFunTarget, - funData.transformer + funData ) origin = key.origin @@ -727,7 +729,7 @@ class SuspendTransformFirTransformer( ) // copy完了再resolve,这样里面包的type parameter就不会有问题了(如果有type parameter的话) - val resolvedReturnType = resolveReturnType(funData.transformer, copiedReturnType) + val resolvedReturnType = resolveReturnType(funData, copiedReturnType) val newFunTarget = FirFunctionTarget(null, isLambda = false) @@ -808,7 +810,7 @@ class SuspendTransformFirTransformer( thisValueParameters, funData.transformerFunctionSymbol, newFunTarget, - funData.transformer + funData ) }.also { getter -> newFunTarget.bind(getter) @@ -963,6 +965,7 @@ class SuspendTransformFirTransformer( val anno = firAnnotation(func, markAnnotation, classSymbol) ?: continue + val markAnnotationTypeArgument = anno.typeArguments.firstOrNull() val transformerFunctionSymbol = transformerFunctionSymbolMap[transformer] ?: error("Cannot find transformer function symbol for transformer: $transformer in $platform") @@ -982,7 +985,8 @@ class SuspendTransformFirTransformer( syntheticFunName, annoData, transformer, - transformerFunctionSymbol + transformerFunctionSymbol, + markAnnotationTypeArgument ) } } @@ -1020,10 +1024,18 @@ class SuspendTransformFirTransformer( )?.firstOrNull() private fun resolveReturnType( - transformer: Transformer, + funData: SyntheticFunData, returnTypeRef: FirTypeRef ): FirTypeRef { - val resultConeType = resolveReturnConeType(transformer, returnTypeRef) + val transformer = funData.transformer + val returnTypeArg = funData.markAnnotationTypeArgument + + val returnTypeConeType = returnTypeArg?.toConeTypeProjection()?.let { + // if is star projection, use Any or Nothing? and the nullable? + it.type ?: session.builtinTypes.nothingType.coneType + } ?: returnTypeRef.coneType + + val resultConeType: ConeKotlinType = resolveReturnConeType(transformer, returnTypeConeType) return if (resultConeType is ConeErrorType) { buildErrorTypeRef { @@ -1039,15 +1051,15 @@ class SuspendTransformFirTransformer( private fun resolveReturnConeType( transformer: Transformer, - returnTypeRef: FirTypeRef + returnTypeConeType: ConeKotlinType ): ConeKotlinType { val returnType = transformer.transformReturnType - ?: return returnTypeRef.coneType // OrNull // original.symbol.resolvedReturnType + ?: return returnTypeConeType // OrNull // original.symbol.resolvedReturnType var typeArguments: Array = emptyArray() if (transformer.transformReturnTypeGeneric) { - typeArguments = arrayOf(ConeKotlinTypeProjectionOut(returnTypeRef.coneType)) + typeArguments = arrayOf(ConeKotlinTypeProjectionOut(returnTypeConeType)) } val resultConeType = returnType.toClassId().createConeType( @@ -1065,6 +1077,7 @@ class SuspendTransformFirTransformer( private fun copyAnnotations( original: FirSimpleFunction, syntheticFunData: SyntheticFunData, ): CopyAnnotations { + // TODO Do not copy marker annotation itself. val transformer = syntheticFunData.transformer val originalAnnotationClassIdMap = original.annotations.keysToMap { it.toAnnotationClassId(session) } diff --git a/compiler/suspend-transform-plugin/src/test-gen/love/forte/plugin/suspendtrans/runners/CodeGenTestRunnerGenerated.java b/compiler/suspend-transform-plugin/src/test-gen/love/forte/plugin/suspendtrans/runners/CodeGenTestRunnerGenerated.java index ec042435..7cf7c915 100644 --- a/compiler/suspend-transform-plugin/src/test-gen/love/forte/plugin/suspendtrans/runners/CodeGenTestRunnerGenerated.java +++ b/compiler/suspend-transform-plugin/src/test-gen/love/forte/plugin/suspendtrans/runners/CodeGenTestRunnerGenerated.java @@ -85,4 +85,10 @@ public void testVarargParam() { public void testMarkName() { runTest("src/testData/codegen/markName.kt"); } + + @Test + @TestMetadata("typedAnno.kt") + public void typedAnno() { + runTest("src/testData/codegen/typedAnno.kt"); + } } diff --git a/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt b/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt index c2bdc0ac..3c221cab 100644 --- a/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt +++ b/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt @@ -1,11 +1,17 @@ package love.forte.plugin.suspendtrans.services import love.forte.plugin.suspendtrans.SuspendTransformComponentRegistrar -import love.forte.plugin.suspendtrans.configuration.InternalSuspendTransformConfigurationApi +import love.forte.plugin.suspendtrans.configuration.* import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jsPromiseTransformer +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmApi4JAnnotationClassInfo +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmAsyncMarkAnnotationClassInfo import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmAsyncTransformer +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmBlockingMarkAnnotationClassInfo +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmBlockingTransformFunction import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmBlockingTransformer -import love.forte.plugin.suspendtrans.configuration.TargetPlatform +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmNameAnnotationClassInfo +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmSyntheticClassInfo +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.kotlinOptInClassInfo import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar @@ -31,7 +37,43 @@ class SuspendTransformerEnvironmentConfigurator(testServices: TestServices) : En val testConfiguration = love.forte.plugin.suspendtrans.configuration.SuspendTransformConfiguration( transformers = mapOf( TargetPlatform.JS to listOf(jsPromiseTransformer), - TargetPlatform.JVM to listOf(jvmBlockingTransformer, jvmAsyncTransformer) + TargetPlatform.JVM to listOf( + jvmBlockingTransformer, + jvmAsyncTransformer, + // Typed Mark Annotation + Transformer( + markAnnotation = MarkAnnotation( + classInfo = ClassInfo( + packageName = "love.forte.plugin.suspendtrans.annotation", + className = "JvmBlockingWithType" + ), + defaultSuffix = "Blocking", + markNameProperty = MarkNameProperty( + propertyName = "markName", + annotation = jvmNameAnnotationClassInfo, + annotationMarkNamePropertyName = "name" + ) + ), + transformFunctionInfo = jvmBlockingTransformFunction, + transformReturnType = null, + transformReturnTypeGeneric = false, + originFunctionIncludeAnnotations = listOf(IncludeAnnotation(jvmSyntheticClassInfo)), + syntheticFunctionIncludeAnnotations = listOf( + IncludeAnnotation( + classInfo = jvmApi4JAnnotationClassInfo, + includeProperty = true + ) + ), + copyAnnotationsToSyntheticFunction = true, + copyAnnotationExcludes = listOf( + jvmSyntheticClassInfo, + jvmBlockingMarkAnnotationClassInfo, + jvmAsyncMarkAnnotationClassInfo, + kotlinOptInClassInfo, + jvmNameAnnotationClassInfo, + ), + ) + ) ) ) // register plugin @@ -58,6 +100,11 @@ class SuspendTransformerEnvironmentConfigurator(testServices: TestServices) : En it ) } + getRuntimeJarFile("love.forte.plugin.suspendtrans.annotation.JvmBlockingWithType")?.let { + configuration.addJvmClasspathRoot( + it + ) + } // register coroutines getRuntimeJarFile("kotlinx.coroutines.CoroutineScope")?.let { diff --git a/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.fir.txt b/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.fir.txt new file mode 100644 index 00000000..734e1e7d --- /dev/null +++ b/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.fir.txt @@ -0,0 +1,30 @@ +FILE: Main.kt + public abstract interface Inter : R|kotlin/Any| { + @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType|() @R|kotlin/jvm/JvmSynthetic|() public abstract suspend fun runner(): R|kotlin/Result| + + @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType|() @R|kotlin/jvm/JvmSynthetic|() public abstract suspend fun runner1(): R|kotlin/Result| + + @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType<*>|() @R|kotlin/jvm/JvmSynthetic|() public abstract suspend fun runnerStar(): R|kotlin/Result| + + @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType|() @R|love/forte/plugin/suspendtrans/annotation/Api4J|() public open fun runnerBlocking(): R|kotlin/String| { + ^runnerBlocking R|love/forte/plugin/suspendtrans/runtime/$runInBlocking$|(suspend fun (): R|kotlin/Result| { + ^ this@R|/Inter|.R|/Inter.runner|() + } + ) + } + + @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType|() @R|love/forte/plugin/suspendtrans/annotation/Api4J|() public open fun runner1Blocking(): R|T| { + ^runner1Blocking R|love/forte/plugin/suspendtrans/runtime/$runInBlocking$|(suspend fun (): R|kotlin/Result| { + ^ this@R|/Inter|.R|/Inter.runner1|() + } + ) + } + + @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType<*>|() @R|love/forte/plugin/suspendtrans/annotation/Api4J|() public open fun runnerStarBlocking(): R|kotlin/Nothing| { + ^runnerStarBlocking R|love/forte/plugin/suspendtrans/runtime/$runInBlocking$|(suspend fun (): R|kotlin/Result| { + ^ this@R|/Inter|.R|/Inter.runnerStar|() + } + ) + } + + } diff --git a/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt b/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt new file mode 100644 index 00000000..3398d669 --- /dev/null +++ b/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt @@ -0,0 +1,18 @@ +// FIR_DUMP +// DUMP_IR +// SOURCE +// FILE: Main.kt [MainKt#main] +import love.forte.plugin.suspendtrans.annotation.JvmAsync +import love.forte.plugin.suspendtrans.annotation.JvmBlocking +import love.forte.plugin.suspendtrans.annotation.JvmBlockingWithType + +interface Inter { + @JvmBlockingWithType + suspend fun runner(): kotlin.Result + + @JvmBlockingWithType + suspend fun runner1(): kotlin.Result + + @JvmBlockingWithType<*> + suspend fun runnerStar(): kotlin.Result +} \ No newline at end of file diff --git a/runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt b/runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt new file mode 100644 index 00000000..6c5be81c --- /dev/null +++ b/runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt @@ -0,0 +1,13 @@ +package love.forte.plugin.suspendtrans.annotation + +@OptIn(ExperimentalMultiplatform::class) +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +@OptionalExpectation +public expect annotation class JvmBlockingWithType( + val type: String = "", + val baseName: String = "", + val suffix: String = "Blocking", + val asProperty: Boolean = false, + val markName: String = "", +) diff --git a/runtime/suspend-transform-annotation/src/jvmMain/kotlin/love.forte.plugin.suspendtrans/annotation/TypedJvmBlocking.jvm.kt b/runtime/suspend-transform-annotation/src/jvmMain/kotlin/love.forte.plugin.suspendtrans/annotation/TypedJvmBlocking.jvm.kt new file mode 100644 index 00000000..271d9b77 --- /dev/null +++ b/runtime/suspend-transform-annotation/src/jvmMain/kotlin/love.forte.plugin.suspendtrans/annotation/TypedJvmBlocking.jvm.kt @@ -0,0 +1,12 @@ +package love.forte.plugin.suspendtrans.annotation + +@OptIn(ExperimentalMultiplatform::class) +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +public actual annotation class JvmBlockingWithType( + actual val type: String = "", + actual val baseName: String = "", + actual val suffix: String = "Blocking", + actual val asProperty: Boolean = false, + actual val markName: String = "", +) From 03ca25e282cf024b2d586898fc28130349610a01 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 8 Jul 2025 16:25:16 +0800 Subject: [PATCH 02/10] feat: Introduce `ExperimentalReturnTypeOverrideGenericApi`, add support for generic-based return type overrides, and update related configurations and annotations. --- buildSrc/src/main/kotlin/IProject.kt | 2 +- .../build.gradle.kts | 32 ++++++++ ...xperimentalReturnTypeOverrideGenericApi.kt | 20 +++++ .../build.gradle.kts | 2 +- .../SuspendTransformConfiguration.kt | 62 ++++++++++++++- .../fir/SuspendTransformFirTransformer.kt | 78 ++++++++++++------- ...spendTransformerEnvironmentConfigurator.kt | 5 +- .../build.gradle.kts | 2 +- .../gradle/SuspendTransformPluginExtension.kt | 57 +++++++++++++- settings.gradle.kts | 3 +- tests/build.gradle.kts | 2 +- .../plugin/suspendtrans/sample/GenericAnno.kt | 12 +++ 12 files changed, 233 insertions(+), 44 deletions(-) create mode 100644 compiler/suspend-transform-plugin-annotation/build.gradle.kts create mode 100644 compiler/suspend-transform-plugin-annotation/src/main/kotlin/love/forte/plugin/suspendtrans/annotation/ExperimentalReturnTypeOverrideGenericApi.kt create mode 100644 tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/GenericAnno.kt diff --git a/buildSrc/src/main/kotlin/IProject.kt b/buildSrc/src/main/kotlin/IProject.kt index fcf255db..5dbb1a4f 100644 --- a/buildSrc/src/main/kotlin/IProject.kt +++ b/buildSrc/src/main/kotlin/IProject.kt @@ -11,7 +11,7 @@ object IProject : ProjectDetail() { // Remember the libs.versions.toml! val ktVersion = "2.2.0" - val pluginVersion = "0.13.1" + val pluginVersion = "0.14.0" override val version: String = "$ktVersion-$pluginVersion" diff --git a/compiler/suspend-transform-plugin-annotation/build.gradle.kts b/compiler/suspend-transform-plugin-annotation/build.gradle.kts new file mode 100644 index 00000000..884ed868 --- /dev/null +++ b/compiler/suspend-transform-plugin-annotation/build.gradle.kts @@ -0,0 +1,32 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + kotlin("jvm") + id("suspend-transform.maven-publish") +} + +dependencies { + testImplementation(kotlin("test")) +} + +kotlin { + configGradleBuildSrcFriendly() + compilerOptions { + jvmTarget.set(JvmTarget.JVM_1_8) + } + +} + +tasks.test { + useJUnitPlatform() +} + +repositories { + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots/") + mavenContent { + snapshotsOnly() + } + } +} + diff --git a/compiler/suspend-transform-plugin-annotation/src/main/kotlin/love/forte/plugin/suspendtrans/annotation/ExperimentalReturnTypeOverrideGenericApi.kt b/compiler/suspend-transform-plugin-annotation/src/main/kotlin/love/forte/plugin/suspendtrans/annotation/ExperimentalReturnTypeOverrideGenericApi.kt new file mode 100644 index 00000000..67b83c94 --- /dev/null +++ b/compiler/suspend-transform-plugin-annotation/src/main/kotlin/love/forte/plugin/suspendtrans/annotation/ExperimentalReturnTypeOverrideGenericApi.kt @@ -0,0 +1,20 @@ +package love.forte.plugin.suspendtrans.annotation + +/** + * The API related to _return type override generic_ is experimental. + * It may be changed in the future without notice. + * + * @since 0.14.0 + */ +@RequiresOptIn( + message = "This API is experimental. It may be changed in the future without notice.", + level = RequiresOptIn.Level.ERROR +) +@Target( + AnnotationTarget.CLASS, + AnnotationTarget.CONSTRUCTOR, + AnnotationTarget.FUNCTION, + AnnotationTarget.PROPERTY, + AnnotationTarget.VALUE_PARAMETER, +) +annotation class ExperimentalReturnTypeOverrideGenericApi \ No newline at end of file diff --git a/compiler/suspend-transform-plugin-configuration/build.gradle.kts b/compiler/suspend-transform-plugin-configuration/build.gradle.kts index 2459051b..ba716b10 100644 --- a/compiler/suspend-transform-plugin-configuration/build.gradle.kts +++ b/compiler/suspend-transform-plugin-configuration/build.gradle.kts @@ -9,7 +9,7 @@ plugins { dependencies { api(libs.kotlinx.serialization.core) - // api(libs.kotlinx.serialization.protobuf) + api(project(":compiler:suspend-transform-plugin-annotation")) testImplementation(kotlin("test")) } diff --git a/compiler/suspend-transform-plugin-configuration/src/main/kotlin/love/forte/plugin/suspendtrans/configuration/SuspendTransformConfiguration.kt b/compiler/suspend-transform-plugin-configuration/src/main/kotlin/love/forte/plugin/suspendtrans/configuration/SuspendTransformConfiguration.kt index 520e348d..f16b12b5 100644 --- a/compiler/suspend-transform-plugin-configuration/src/main/kotlin/love/forte/plugin/suspendtrans/configuration/SuspendTransformConfiguration.kt +++ b/compiler/suspend-transform-plugin-configuration/src/main/kotlin/love/forte/plugin/suspendtrans/configuration/SuspendTransformConfiguration.kt @@ -1,6 +1,7 @@ package love.forte.plugin.suspendtrans.configuration import kotlinx.serialization.Serializable +import love.forte.plugin.suspendtrans.annotation.ExperimentalReturnTypeOverrideGenericApi // NOTE: // 配置信息均使用 `Protobuf` 进行序列化 @@ -80,6 +81,7 @@ enum class TargetPlatform { * 用于标记的注解信息, 例如 `@JvmBlocking`, `@JvmAsync`, `@JsPromise`. */ @Serializable +@OptIn(ExperimentalReturnTypeOverrideGenericApi::class) class MarkAnnotation @InternalSuspendTransformConfigurationApi constructor( /** * 注解类信息 @@ -113,12 +115,60 @@ class MarkAnnotation @InternalSuspendTransformConfigurationApi constructor( */ // 'null' is not supported for optional properties in ProtoBuf val markNameProperty: MarkNameProperty?, + + /** + * Indicates whether there is a generic type used to override the function return type + * on the current mark annotation. + * + * If `true`, when determining the return type of the generated function, + * the content of this generic type will be directly regarded as the return type of the origin function, + * rather than the actual type of the origin function. + * + * For example, Normally, the return type of the generated function + * depends on the return type of the origin function: + * + * ```Kotlin + * @JvmBlocking + * suspend fun run(): Result + * + * // Generated + * + * @Api4J + * fun runBlocking(): Result + * ``` + * + * However, if the annotation has a generic type and `hasReturnTypeOverrideGeneric` is true: + * + * ```Kotlin + * @JvmBlockingWithType + * suspend fun run(): Result + * + * // Generated + * + * @Api4J + * fun runBlocking(): String? + * ``` + * + * As can be seen, the generated function ignores the actual return type of the origin function + * and instead treats the generic type specified in the annotation as the return type + * of the origin function. + * + * Note: If you want to determine the return type through type overloading, + * you must ensure that the transformer function you use correctly matches the input and output parameter types. + * Otherwise, it may result in compilation errors or runtime exceptions. + * + * @since 0.14.0 + */ + @property:ExperimentalReturnTypeOverrideGenericApi + val hasReturnTypeOverrideGeneric: Boolean, ) { + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is MarkAnnotation) return false if (defaultAsProperty != other.defaultAsProperty) return false + if (hasReturnTypeOverrideGeneric != other.hasReturnTypeOverrideGeneric) return false if (classInfo != other.classInfo) return false if (baseNameProperty != other.baseNameProperty) return false if (suffixProperty != other.suffixProperty) return false @@ -131,6 +181,7 @@ class MarkAnnotation @InternalSuspendTransformConfigurationApi constructor( override fun hashCode(): Int { var result = defaultAsProperty.hashCode() + result = 31 * result + hasReturnTypeOverrideGeneric.hashCode() result = 31 * result + classInfo.hashCode() result = 31 * result + baseNameProperty.hashCode() result = 31 * result + suffixProperty.hashCode() @@ -141,7 +192,7 @@ class MarkAnnotation @InternalSuspendTransformConfigurationApi constructor( } override fun toString(): String { - return "MarkAnnotation(asPropertyProperty='$asPropertyProperty', classInfo=$classInfo, baseNameProperty='$baseNameProperty', suffixProperty='$suffixProperty', defaultSuffix='$defaultSuffix', defaultAsProperty=$defaultAsProperty, markName=$markNameProperty)" + return "MarkAnnotation(asPropertyProperty='$asPropertyProperty', classInfo=$classInfo, baseNameProperty='$baseNameProperty', suffixProperty='$suffixProperty', defaultSuffix='$defaultSuffix', defaultAsProperty=$defaultAsProperty, markNameProperty=$markNameProperty, hasReturnTypeOverrideGeneric=$hasReturnTypeOverrideGeneric)" } } @@ -457,7 +508,8 @@ object SuspendTransformConfigurations { propertyName = "markName", annotation = jvmNameAnnotationClassInfo, annotationMarkNamePropertyName = "name" - ) + ), + hasReturnTypeOverrideGeneric = false ) @JvmStatic @@ -480,7 +532,8 @@ object SuspendTransformConfigurations { propertyName = "markName", annotation = jvmNameAnnotationClassInfo, annotationMarkNamePropertyName = "name" - ) + ), + hasReturnTypeOverrideGeneric = false ) @JvmStatic @@ -577,7 +630,8 @@ object SuspendTransformConfigurations { propertyName = "markName", annotation = jsNameAnnotationClassInfo, annotationMarkNamePropertyName = "name" - ) + ), + hasReturnTypeOverrideGeneric = false ) @JvmStatic diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt index 4cdc183b..385207b6 100644 --- a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt @@ -1,5 +1,6 @@ package love.forte.plugin.suspendtrans.fir +import love.forte.plugin.suspendtrans.annotation.ExperimentalReturnTypeOverrideGenericApi import love.forte.plugin.suspendtrans.configuration.MarkAnnotation import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfiguration import love.forte.plugin.suspendtrans.configuration.TargetPlatform @@ -116,6 +117,42 @@ class SuspendTransformFirTransformer( } + private fun findTransformerFunctionSymbol(transformer: Transformer): FirNamedFunctionSymbol? { + val symbolProvider = session.symbolProvider + val dependenciesSymbolProvider = session.dependenciesSymbolProvider + + val transformFunctionInfo = transformer.transformFunctionInfo + val packageName = transformFunctionInfo.packageName + val functionName = transformFunctionInfo.functionName + + val functionNameIdentifier = Name.identifier(functionName) + + val transformerFunctionSymbols = + symbolProvider.getTopLevelFunctionSymbols( + packageName.fqn, + functionNameIdentifier + ).ifEmpty { + dependenciesSymbolProvider.getTopLevelFunctionSymbols( + packageName.fqn, + functionNameIdentifier + ) + } + + if (transformerFunctionSymbols.isNotEmpty()) { + if (transformerFunctionSymbols.size == 1) { + transformerFunctionSymbolMap[transformer] = transformerFunctionSymbols.first() + return transformerFunctionSymbols.first() + } else { + error("Found multiple transformer function symbols for transformer: $transformer") + } + } else { + // 有时候在不同平台中寻找,可能会找不到,例如在jvm中找不到js的函数 + // error("Cannot find transformer function symbol $packageName.$functionName (${session.moduleData.platform}) for transformer: $transformer") + } + + return null + } + private fun initTransformerFunctionSymbolMap( classSymbol: FirClassSymbol<*>, memberScope: FirClassDeclaredMemberScope? @@ -129,35 +166,9 @@ class SuspendTransformFirTransformer( suspendTransformConfiguration.transformers .forEach { (_, transformerList) -> for (transformer in transformerList) { - val transformFunctionInfo = transformer.transformFunctionInfo - val packageName = transformFunctionInfo.packageName - val functionName = transformFunctionInfo.functionName - - // TODO 校验funcs? - - val functionNameIdentifier = Name.identifier(functionName) - - val transformerFunctionSymbols = - symbolProvider.getTopLevelFunctionSymbols( - packageName.fqn, - functionNameIdentifier - ).ifEmpty { - dependenciesSymbolProvider.getTopLevelFunctionSymbols( - packageName.fqn, - functionNameIdentifier - ) - } - - if (transformerFunctionSymbols.isNotEmpty()) { - if (transformerFunctionSymbols.size == 1) { - transformerFunctionSymbolMap[transformer] = transformerFunctionSymbols.first() - map[transformer] = transformerFunctionSymbols.first() - } else { - error("Found multiple transformer function symbols for transformer: $transformer") - } - } else { - // 有时候在不同平台中寻找,可能会找不到,例如在jvm中找不到js的函数 - // error("Cannot find transformer function symbol $packageName.$functionName (${session.moduleData.platform}) for transformer: $transformer") + val transformerFunctionSymbol = findTransformerFunctionSymbol(transformer) + if (transformerFunctionSymbol != null) { + map[transformer] = transformerFunctionSymbol } } } @@ -965,8 +976,15 @@ class SuspendTransformFirTransformer( val anno = firAnnotation(func, markAnnotation, classSymbol) ?: continue - val markAnnotationTypeArgument = anno.typeArguments.firstOrNull() + @OptIn(ExperimentalReturnTypeOverrideGenericApi::class) + val markAnnotationTypeArgument = if (markAnnotation.hasReturnTypeOverrideGeneric) { + anno.typeArguments.firstOrNull() + } else { + null + } + // TODO 也许错误延后抛出,缓存里找不到的话即用即找,可以解决无法在当前模块下使用的问题? + // see https://github.com/ForteScarlet/kotlin-suspend-transform-compiler-plugin/issues/100 val transformerFunctionSymbol = transformerFunctionSymbolMap[transformer] ?: error("Cannot find transformer function symbol for transformer: $transformer in $platform") diff --git a/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt b/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt index 3c221cab..7ad9db80 100644 --- a/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt +++ b/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt @@ -34,7 +34,7 @@ class SuspendTransformerEnvironmentConfigurator(testServices: TestServices) : En module: TestModule, configuration: CompilerConfiguration ) { - val testConfiguration = love.forte.plugin.suspendtrans.configuration.SuspendTransformConfiguration( + val testConfiguration = SuspendTransformConfiguration( transformers = mapOf( TargetPlatform.JS to listOf(jsPromiseTransformer), TargetPlatform.JVM to listOf( @@ -52,7 +52,8 @@ class SuspendTransformerEnvironmentConfigurator(testServices: TestServices) : En propertyName = "markName", annotation = jvmNameAnnotationClassInfo, annotationMarkNamePropertyName = "name" - ) + ), + hasReturnTypeOverrideGeneric = true, ), transformFunctionInfo = jvmBlockingTransformFunction, transformReturnType = null, diff --git a/plugins/suspend-transform-plugin-gradle/build.gradle.kts b/plugins/suspend-transform-plugin-gradle/build.gradle.kts index dbad3c82..bc775ed9 100644 --- a/plugins/suspend-transform-plugin-gradle/build.gradle.kts +++ b/plugins/suspend-transform-plugin-gradle/build.gradle.kts @@ -3,7 +3,6 @@ import love.forte.gradle.common.publication.configure.configPublishMaven import love.forte.gradle.common.publication.configure.publishingExtension import love.forte.gradle.common.publication.configure.setupPom import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import utils.isMainPublishable @@ -32,6 +31,7 @@ dependencies { compileOnly(kotlin("gradle-plugin-api")) // compileOnly(project(":compiler:suspend-transform-plugin")) api(project(":compiler:suspend-transform-plugin-cli")) + api(project(":compiler:suspend-transform-plugin-annotation")) api(project(":compiler:suspend-transform-plugin-configuration")) api(project(":compiler:suspend-transform-plugin-deprecated-configuration")) } diff --git a/plugins/suspend-transform-plugin-gradle/src/main/kotlin/love/forte/plugin/suspendtrans/gradle/SuspendTransformPluginExtension.kt b/plugins/suspend-transform-plugin-gradle/src/main/kotlin/love/forte/plugin/suspendtrans/gradle/SuspendTransformPluginExtension.kt index 3f02b92d..2a82c3db 100644 --- a/plugins/suspend-transform-plugin-gradle/src/main/kotlin/love/forte/plugin/suspendtrans/gradle/SuspendTransformPluginExtension.kt +++ b/plugins/suspend-transform-plugin-gradle/src/main/kotlin/love/forte/plugin/suspendtrans/gradle/SuspendTransformPluginExtension.kt @@ -1,5 +1,6 @@ package love.forte.plugin.suspendtrans.gradle +import love.forte.plugin.suspendtrans.annotation.ExperimentalReturnTypeOverrideGenericApi import love.forte.plugin.suspendtrans.configuration.* import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jsPromiseTransformer import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmAsyncTransformer @@ -781,6 +782,54 @@ abstract class MarkAnnotationSpec */ abstract val markNameProperty: Property + /** + * Indicates whether there is a generic type used to override the function return type + * on the current mark annotation. + * + * If `true`, when determining the return type of the generated function, + * the content of this generic type will be directly regarded as the return type of the origin function, + * rather than the actual type of the origin function. + * + * For example, Normally, the return type of the generated function + * depends on the return type of the origin function: + * + * ```Kotlin + * @JvmBlocking + * suspend fun run(): Result + * + * // Generated + * + * @Api4J + * fun runBlocking(): Result + * ``` + * + * However, if the annotation has a generic type and `hasReturnTypeOverrideGeneric` is true: + * + * ```Kotlin + * @JvmBlockingWithType + * suspend fun run(): Result + * + * // Generated + * + * @Api4J + * fun runBlocking(): String? + * ``` + * + * As can be seen, the generated function ignores the actual return type of the origin function + * and instead treats the generic type specified in the annotation as the return type + * of the origin function. + * + * Note: If you want to determine the return type through type overloading, + * you must ensure that the transformer function you use correctly matches the input and output parameter types. + * Otherwise, it may result in compilation errors or runtime exceptions. + * + * @since 0.14.0 + */ + @ExperimentalReturnTypeOverrideGenericApi + val hasReturnTypeOverrideGeneric: Property = + objects.property(Boolean::class.java).convention(false) + + fun markNameProperty(action: Action) { markNameProperty.set(markNameProperty.getOrElse(objects.newInstance()).also(action::execute)) } @@ -789,6 +838,7 @@ abstract class MarkAnnotationSpec markNameProperty.set(markNameProperty.getOrElse(objects.newInstance()).also(action)) } + @OptIn(ExperimentalReturnTypeOverrideGenericApi::class) fun from(markAnnotation: MarkAnnotation) { classInfo { from(markAnnotation.classInfo) @@ -805,7 +855,7 @@ abstract class MarkAnnotationSpec } } - + hasReturnTypeOverrideGeneric.set(markAnnotation.hasReturnTypeOverrideGeneric) } } @@ -992,7 +1042,7 @@ internal fun TransformerSpec.toTransformer(): Transformer { ) } -@OptIn(InternalSuspendTransformConfigurationApi::class) +@OptIn(InternalSuspendTransformConfigurationApi::class, ExperimentalReturnTypeOverrideGenericApi::class) internal fun MarkAnnotationSpec.toMarkAnnotation(): MarkAnnotation { return MarkAnnotation( classInfo = classInfo.get().toClassInfo(), @@ -1001,7 +1051,8 @@ internal fun MarkAnnotationSpec.toMarkAnnotation(): MarkAnnotation { asPropertyProperty = asPropertyProperty.get(), defaultSuffix = defaultSuffix.get(), defaultAsProperty = defaultAsProperty.get(), - markNameProperty = markNameProperty.orNull?.toMarkNameProperty() + markNameProperty = markNameProperty.orNull?.toMarkNameProperty(), + hasReturnTypeOverrideGeneric = hasReturnTypeOverrideGeneric.getOrElse(false) ) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 9996bf86..09b99cc6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -25,6 +25,7 @@ include(":compiler:suspend-transform-plugin") include(":compiler:suspend-transform-plugin-cli") include(":compiler:suspend-transform-plugin-deprecated-configuration") include(":compiler:suspend-transform-plugin-configuration") +include(":compiler:suspend-transform-plugin-annotation") include(":compiler:suspend-transform-plugin-embeddable") include(":runtime:suspend-transform-annotation") @@ -35,7 +36,7 @@ include(":plugins:suspend-transform-plugin-gradle") // include(":local-helper") //Samples -//include(":tests:test-jvm") +include(":tests:test-jvm") //include(":tests:test-js") //include(":tests:test-kmp") // include(":tests:test-android") diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index 258312d0..015d4db3 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -1,3 +1,3 @@ plugins { - id("love.forte.plugin.suspend-transform") version "2.2.0-RC2-0.13.0" apply false + id("love.forte.plugin.suspend-transform") version "2.2.0-0.13.1" apply false } diff --git a/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/GenericAnno.kt b/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/GenericAnno.kt new file mode 100644 index 00000000..2989ea59 --- /dev/null +++ b/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/GenericAnno.kt @@ -0,0 +1,12 @@ +package love.forte.plugin.suspendtrans.sample + +import kotlinx.coroutines.delay + +annotation class GenericAnno() + + +@GenericAnno() +suspend fun run(): Result { + delay(1) + return Result.success("ok") +} \ No newline at end of file From a4d77739ce808f4e13dc94bcd18e2efbd1f3cc98 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Wed, 9 Jul 2025 17:54:54 +0800 Subject: [PATCH 03/10] test: Remove `JvmBlockingWithType` annotation, cleanup related configurations, and introduce `test-runner` module with generic return type support. --- ...spendTransformerEnvironmentConfigurator.kt | 50 +----------- .../src/testData/codegen/typedAnno.fir.txt | 30 -------- .../src/testData/codegen/typedAnno.kt | 18 ----- .../gradle/SuspendTransformGradlePlugin.kt | 1 + .../annotation/JvmBlockingWithType.kt | 13 ---- .../annotation/TypedJvmBlocking.jvm.kt | 12 --- settings.gradle.kts | 3 +- tests/build.gradle.kts | 2 +- tests/test-js/build.gradle.kts | 21 +++++ tests/test-jvm/build.gradle.kts | 41 ++++++++++ .../returntypeoverride/ReturnTypeOverrides.kt | 23 ++++++ tests/test-runner/build.gradle.kts | 77 +++++++++++++++++++ .../suspendtrans/test/runner/Annotations.kt | 28 +++++++ .../test/runner/Annotations.js.kt | 9 +++ .../suspendtrans/test/runner/JsRunner.kt | 12 +++ .../test/runner/Annotations.jvm.kt | 17 ++++ .../suspendtrans/test/runner/JvmRunner.kt | 20 +++++ 17 files changed, 255 insertions(+), 122 deletions(-) delete mode 100644 compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.fir.txt delete mode 100644 compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt delete mode 100644 runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt delete mode 100644 runtime/suspend-transform-annotation/src/jvmMain/kotlin/love.forte.plugin.suspendtrans/annotation/TypedJvmBlocking.jvm.kt create mode 100644 tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/returntypeoverride/ReturnTypeOverrides.kt create mode 100644 tests/test-runner/build.gradle.kts create mode 100644 tests/test-runner/src/commonMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.kt create mode 100644 tests/test-runner/src/jsMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.js.kt create mode 100644 tests/test-runner/src/jsMain/kotlin/love/forte/suspendtrans/test/runner/JsRunner.kt create mode 100644 tests/test-runner/src/jvmMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.jvm.kt create mode 100644 tests/test-runner/src/jvmMain/kotlin/love/forte/suspendtrans/test/runner/JvmRunner.kt diff --git a/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt b/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt index 7ad9db80..30f108c6 100644 --- a/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt +++ b/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt @@ -1,17 +1,12 @@ package love.forte.plugin.suspendtrans.services import love.forte.plugin.suspendtrans.SuspendTransformComponentRegistrar -import love.forte.plugin.suspendtrans.configuration.* +import love.forte.plugin.suspendtrans.configuration.InternalSuspendTransformConfigurationApi +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfiguration import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jsPromiseTransformer -import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmApi4JAnnotationClassInfo -import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmAsyncMarkAnnotationClassInfo import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmAsyncTransformer -import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmBlockingMarkAnnotationClassInfo -import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmBlockingTransformFunction import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmBlockingTransformer -import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmNameAnnotationClassInfo -import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmSyntheticClassInfo -import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.kotlinOptInClassInfo +import love.forte.plugin.suspendtrans.configuration.TargetPlatform import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar @@ -40,40 +35,6 @@ class SuspendTransformerEnvironmentConfigurator(testServices: TestServices) : En TargetPlatform.JVM to listOf( jvmBlockingTransformer, jvmAsyncTransformer, - // Typed Mark Annotation - Transformer( - markAnnotation = MarkAnnotation( - classInfo = ClassInfo( - packageName = "love.forte.plugin.suspendtrans.annotation", - className = "JvmBlockingWithType" - ), - defaultSuffix = "Blocking", - markNameProperty = MarkNameProperty( - propertyName = "markName", - annotation = jvmNameAnnotationClassInfo, - annotationMarkNamePropertyName = "name" - ), - hasReturnTypeOverrideGeneric = true, - ), - transformFunctionInfo = jvmBlockingTransformFunction, - transformReturnType = null, - transformReturnTypeGeneric = false, - originFunctionIncludeAnnotations = listOf(IncludeAnnotation(jvmSyntheticClassInfo)), - syntheticFunctionIncludeAnnotations = listOf( - IncludeAnnotation( - classInfo = jvmApi4JAnnotationClassInfo, - includeProperty = true - ) - ), - copyAnnotationsToSyntheticFunction = true, - copyAnnotationExcludes = listOf( - jvmSyntheticClassInfo, - jvmBlockingMarkAnnotationClassInfo, - jvmAsyncMarkAnnotationClassInfo, - kotlinOptInClassInfo, - jvmNameAnnotationClassInfo, - ), - ) ) ) ) @@ -101,11 +62,6 @@ class SuspendTransformerEnvironmentConfigurator(testServices: TestServices) : En it ) } - getRuntimeJarFile("love.forte.plugin.suspendtrans.annotation.JvmBlockingWithType")?.let { - configuration.addJvmClasspathRoot( - it - ) - } // register coroutines getRuntimeJarFile("kotlinx.coroutines.CoroutineScope")?.let { diff --git a/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.fir.txt b/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.fir.txt deleted file mode 100644 index 734e1e7d..00000000 --- a/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.fir.txt +++ /dev/null @@ -1,30 +0,0 @@ -FILE: Main.kt - public abstract interface Inter : R|kotlin/Any| { - @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType|() @R|kotlin/jvm/JvmSynthetic|() public abstract suspend fun runner(): R|kotlin/Result| - - @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType|() @R|kotlin/jvm/JvmSynthetic|() public abstract suspend fun runner1(): R|kotlin/Result| - - @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType<*>|() @R|kotlin/jvm/JvmSynthetic|() public abstract suspend fun runnerStar(): R|kotlin/Result| - - @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType|() @R|love/forte/plugin/suspendtrans/annotation/Api4J|() public open fun runnerBlocking(): R|kotlin/String| { - ^runnerBlocking R|love/forte/plugin/suspendtrans/runtime/$runInBlocking$|(suspend fun (): R|kotlin/Result| { - ^ this@R|/Inter|.R|/Inter.runner|() - } - ) - } - - @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType|() @R|love/forte/plugin/suspendtrans/annotation/Api4J|() public open fun runner1Blocking(): R|T| { - ^runner1Blocking R|love/forte/plugin/suspendtrans/runtime/$runInBlocking$|(suspend fun (): R|kotlin/Result| { - ^ this@R|/Inter|.R|/Inter.runner1|() - } - ) - } - - @R|love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType<*>|() @R|love/forte/plugin/suspendtrans/annotation/Api4J|() public open fun runnerStarBlocking(): R|kotlin/Nothing| { - ^runnerStarBlocking R|love/forte/plugin/suspendtrans/runtime/$runInBlocking$|(suspend fun (): R|kotlin/Result| { - ^ this@R|/Inter|.R|/Inter.runnerStar|() - } - ) - } - - } diff --git a/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt b/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt deleted file mode 100644 index 3398d669..00000000 --- a/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt +++ /dev/null @@ -1,18 +0,0 @@ -// FIR_DUMP -// DUMP_IR -// SOURCE -// FILE: Main.kt [MainKt#main] -import love.forte.plugin.suspendtrans.annotation.JvmAsync -import love.forte.plugin.suspendtrans.annotation.JvmBlocking -import love.forte.plugin.suspendtrans.annotation.JvmBlockingWithType - -interface Inter { - @JvmBlockingWithType - suspend fun runner(): kotlin.Result - - @JvmBlockingWithType - suspend fun runner1(): kotlin.Result - - @JvmBlockingWithType<*> - suspend fun runnerStar(): kotlin.Result -} \ No newline at end of file diff --git a/plugins/suspend-transform-plugin-gradle/src/main/kotlin/love/forte/plugin/suspendtrans/gradle/SuspendTransformGradlePlugin.kt b/plugins/suspend-transform-plugin-gradle/src/main/kotlin/love/forte/plugin/suspendtrans/gradle/SuspendTransformGradlePlugin.kt index 829c4af2..ea47794f 100644 --- a/plugins/suspend-transform-plugin-gradle/src/main/kotlin/love/forte/plugin/suspendtrans/gradle/SuspendTransformGradlePlugin.kt +++ b/plugins/suspend-transform-plugin-gradle/src/main/kotlin/love/forte/plugin/suspendtrans/gradle/SuspendTransformGradlePlugin.kt @@ -222,6 +222,7 @@ private fun DeprecatedMarkAnnotation.toMarkAnnotation(): MarkAnnotation { defaultSuffix = this.defaultSuffix, defaultAsProperty = this.defaultAsProperty, markNameProperty = null, + hasReturnTypeOverrideGeneric = false ) } diff --git a/runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt b/runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt deleted file mode 100644 index 6c5be81c..00000000 --- a/runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt +++ /dev/null @@ -1,13 +0,0 @@ -package love.forte.plugin.suspendtrans.annotation - -@OptIn(ExperimentalMultiplatform::class) -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) -@Retention(AnnotationRetention.BINARY) -@OptionalExpectation -public expect annotation class JvmBlockingWithType( - val type: String = "", - val baseName: String = "", - val suffix: String = "Blocking", - val asProperty: Boolean = false, - val markName: String = "", -) diff --git a/runtime/suspend-transform-annotation/src/jvmMain/kotlin/love.forte.plugin.suspendtrans/annotation/TypedJvmBlocking.jvm.kt b/runtime/suspend-transform-annotation/src/jvmMain/kotlin/love.forte.plugin.suspendtrans/annotation/TypedJvmBlocking.jvm.kt deleted file mode 100644 index 271d9b77..00000000 --- a/runtime/suspend-transform-annotation/src/jvmMain/kotlin/love.forte.plugin.suspendtrans/annotation/TypedJvmBlocking.jvm.kt +++ /dev/null @@ -1,12 +0,0 @@ -package love.forte.plugin.suspendtrans.annotation - -@OptIn(ExperimentalMultiplatform::class) -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) -@Retention(AnnotationRetention.BINARY) -public actual annotation class JvmBlockingWithType( - actual val type: String = "", - actual val baseName: String = "", - actual val suffix: String = "Blocking", - actual val asProperty: Boolean = false, - actual val markName: String = "", -) diff --git a/settings.gradle.kts b/settings.gradle.kts index 09b99cc6..5432af90 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -37,6 +37,7 @@ include(":plugins:suspend-transform-plugin-gradle") //Samples include(":tests:test-jvm") -//include(":tests:test-js") +include(":tests:test-js") +include(":tests:test-runner") //include(":tests:test-kmp") // include(":tests:test-android") diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index 015d4db3..b43d671c 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -1,3 +1,3 @@ plugins { - id("love.forte.plugin.suspend-transform") version "2.2.0-0.13.1" apply false + id("love.forte.plugin.suspend-transform") version "2.2.0-0.14.0" apply false } diff --git a/tests/test-js/build.gradle.kts b/tests/test-js/build.gradle.kts index aa10898c..5e0ac367 100644 --- a/tests/test-js/build.gradle.kts +++ b/tests/test-js/build.gradle.kts @@ -1,3 +1,6 @@ +@file:OptIn(ExperimentalReturnTypeOverrideGenericApi::class) + +import love.forte.plugin.suspendtrans.annotation.ExperimentalReturnTypeOverrideGenericApi import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.kotlinJsExportIgnoreClassInfo import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi @@ -41,6 +44,7 @@ kotlin { implementation(libs.kotlinx.coroutines.core) implementation(project(":runtime:suspend-transform-annotation")) implementation(project(":runtime:suspend-transform-runtime")) + implementation(project(":tests:test-runner")) } jsTest.dependencies { implementation(kotlin("test")) @@ -64,5 +68,22 @@ suspendTransformPlugin { from(kotlinJsExportIgnoreClassInfo) } } + addJsPromise { + markAnnotation { + classInfo { + packageName = "love.forte.suspendtrans.test.runner" + className = "JsResultPromise" + } + hasReturnTypeOverrideGeneric = true + } + + transformFunctionInfo { + packageName = "love.forte.suspendtrans.test.runner" + functionName = "jsResultToAsync" + } + + + } + // love.forte.suspendtrans.test.runner } } diff --git a/tests/test-jvm/build.gradle.kts b/tests/test-jvm/build.gradle.kts index 743e1005..e5b2b3f6 100644 --- a/tests/test-jvm/build.gradle.kts +++ b/tests/test-jvm/build.gradle.kts @@ -1,3 +1,6 @@ +@file:OptIn(ExperimentalReturnTypeOverrideGenericApi::class) + +import love.forte.plugin.suspendtrans.annotation.ExperimentalReturnTypeOverrideGenericApi import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 plugins { @@ -30,6 +33,7 @@ dependencies { api(project(":runtime:suspend-transform-annotation")) api(project(":runtime:suspend-transform-runtime")) api(libs.kotlinx.coroutines.core) + api(project(":tests:test-runner")) } // @Suppress("DEPRECATION") @@ -45,6 +49,43 @@ suspendTransformPlugin { includeRuntime = false transformers { useDefault() + + addJvmBlocking { + markAnnotation { + classInfo { + packageName = "love.forte.suspendtrans.test.runner" + className = "JvmResultBlock" + } + hasReturnTypeOverrideGeneric = true + } + transformFunctionInfo { + packageName = "love.forte.suspendtrans.test.runner" + functionName = "jvmResultToBlock" + } + // T itself + // transformReturnType { + // } + transformReturnTypeGeneric = false + } + // + // addJvmAsync { + // markAnnotation { + // classInfo { + // packageName = "love.forte.suspendtrans.test.runner" + // className = "JvmResultAsync" + // } + // hasReturnTypeOverrideGeneric = true + // } + // transformFunctionInfo { + // packageName = "love.forte.suspendtrans.test.runner" + // functionName = "jvmResultToAsync" + // } + // // CompletableFuture + // // transformReturnType { + // // } + // transformReturnTypeGeneric = true + // } + } } diff --git a/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/returntypeoverride/ReturnTypeOverrides.kt b/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/returntypeoverride/ReturnTypeOverrides.kt new file mode 100644 index 00000000..8ddf6632 --- /dev/null +++ b/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/returntypeoverride/ReturnTypeOverrides.kt @@ -0,0 +1,23 @@ +package love.forte.plugin.suspendtrans.sample.returntypeoverride + +import kotlinx.coroutines.delay +import love.forte.suspendtrans.test.runner.JvmResultAsync +import love.forte.suspendtrans.test.runner.JvmResultBlock + +interface Foo { + @JvmResultBlock + @JvmResultAsync + suspend fun hello(): Result { + delay(1) + return Result.success("Hello, ") + } + + // TODO e: file://~/suspend-transform-kotlin-compile-plugin/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/returntypeoverride/ReturnTypeOverrides.kt:16:21 Unresolved reference 'T'. + // @JvmResultBlock + // @JvmResultAsync + // suspend fun foo(value: T): Result { + // delay(1) + // return Result.success(value) + // } + +} \ No newline at end of file diff --git a/tests/test-runner/build.gradle.kts b/tests/test-runner/build.gradle.kts new file mode 100644 index 00000000..9e315d48 --- /dev/null +++ b/tests/test-runner/build.gradle.kts @@ -0,0 +1,77 @@ +plugins { + kotlin("multiplatform") +} + +kotlin { + compilerOptions { + freeCompilerArgs.add("-Xjvm-default=all") + freeCompilerArgs.add("-Xexpect-actual-classes") + } +} + +repositories { + mavenLocal() +} + +kotlin { + + jvmToolchain(11) + jvm() + js { + nodejs() + + useEsModules() + generateTypeScriptDefinitions() + binaries.executable() + + compilerOptions { + target = "es2015" + useEsClasses = true + freeCompilerArgs.addAll( + // https://kotlinlang.org/docs/whatsnew20.html#per-file-compilation-for-kotlin-js-projects + "-Xir-per-file", + ) + } + } + + sourceSets { + commonMain.dependencies { + // implementation(kotlin("reflect")) + // implementation(project(":runtime:suspend-transform-annotation")) + // implementation(project(":runtime:suspend-transform-runtime")) + implementation(libs.kotlinx.coroutines.core) + } + + commonTest.dependencies { + implementation(kotlin("test")) + implementation(libs.kotlinx.coroutines.test) + } + } +} + +tasks.withType { + options.encoding = "UTF-8" + sourceCompatibility = "11" + targetCompatibility = "11" + + // see https://kotlinlang.org/docs/gradle-configure-project.html#configure-with-java-modules-jpms-enabled + // if (moduleName != null) { + // options.compilerArgumentProviders.add( + // CommandLineArgumentProvider { + // // Provide compiled Kotlin classes to javac – needed for Java/Kotlin mixed sources to work + // // listOf("--patch-module", "$moduleName=${sourceSets["main"].output.asPath}") + // val sourceSet = sourceSets.findByName("main") ?: sourceSets.findByName("jvmMain") + // if (sourceSet != null) { + // listOf("--patch-module", "$moduleName=${sourceSet.output.asPath}") + // } else { + // emptyList() + // } + // // listOf("--patch-module", "$moduleName=${sourceSets["main"].output.asPath}") + // } + // ) + // } +} + +tasks.withType { + useJUnitPlatform() +} diff --git a/tests/test-runner/src/commonMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.kt b/tests/test-runner/src/commonMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.kt new file mode 100644 index 00000000..c38797ad --- /dev/null +++ b/tests/test-runner/src/commonMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.kt @@ -0,0 +1,28 @@ +package love.forte.suspendtrans.test.runner + +@OptIn(ExperimentalMultiplatform::class) +@OptionalExpectation +@Retention(AnnotationRetention.SOURCE) +expect annotation class JvmResultBlock( + val baseName: String = "", + val suffix: String = "Blocking", + val asProperty: Boolean = false +) + +@OptIn(ExperimentalMultiplatform::class) +@OptionalExpectation +@Retention(AnnotationRetention.SOURCE) +expect annotation class JvmResultAsync( + val baseName: String = "", + val suffix: String = "Async", + val asProperty: Boolean = false +) + +@OptIn(ExperimentalMultiplatform::class) +@OptionalExpectation +@Retention(AnnotationRetention.SOURCE) +expect annotation class JsResultPromise( + val baseName: String = "", + val suffix: String = "Async", + val asProperty: Boolean = false +) \ No newline at end of file diff --git a/tests/test-runner/src/jsMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.js.kt b/tests/test-runner/src/jsMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.js.kt new file mode 100644 index 00000000..95d33480 --- /dev/null +++ b/tests/test-runner/src/jsMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.js.kt @@ -0,0 +1,9 @@ +package love.forte.suspendtrans.test.runner + +@OptIn(markerClass = [ExperimentalMultiplatform::class]) +@Retention(value = AnnotationRetention.SOURCE) +actual annotation class JsResultPromise actual constructor( + actual val baseName: String, + actual val suffix: String, + actual val asProperty: Boolean +) \ No newline at end of file diff --git a/tests/test-runner/src/jsMain/kotlin/love/forte/suspendtrans/test/runner/JsRunner.kt b/tests/test-runner/src/jsMain/kotlin/love/forte/suspendtrans/test/runner/JsRunner.kt new file mode 100644 index 00000000..61480f9f --- /dev/null +++ b/tests/test-runner/src/jsMain/kotlin/love/forte/suspendtrans/test/runner/JsRunner.kt @@ -0,0 +1,12 @@ +package love.forte.suspendtrans.test.runner + +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.promise +import kotlin.js.Promise + + +@OptIn(DelicateCoroutinesApi::class) +fun jsResultToAsync(block: suspend () -> Result): Promise { + return GlobalScope.promise { block().getOrThrow() } +} \ No newline at end of file diff --git a/tests/test-runner/src/jvmMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.jvm.kt b/tests/test-runner/src/jvmMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.jvm.kt new file mode 100644 index 00000000..ffd09514 --- /dev/null +++ b/tests/test-runner/src/jvmMain/kotlin/love/forte/suspendtrans/test/runner/Annotations.jvm.kt @@ -0,0 +1,17 @@ +package love.forte.suspendtrans.test.runner + +@OptIn(markerClass = [ExperimentalMultiplatform::class]) +@Retention(value = AnnotationRetention.SOURCE) +actual annotation class JvmResultBlock actual constructor( + actual val baseName: String, + actual val suffix: String, + actual val asProperty: Boolean +) + +@OptIn(markerClass = [ExperimentalMultiplatform::class]) +@Retention(value = AnnotationRetention.SOURCE) +actual annotation class JvmResultAsync actual constructor( + actual val baseName: String, + actual val suffix: String, + actual val asProperty: Boolean +) \ No newline at end of file diff --git a/tests/test-runner/src/jvmMain/kotlin/love/forte/suspendtrans/test/runner/JvmRunner.kt b/tests/test-runner/src/jvmMain/kotlin/love/forte/suspendtrans/test/runner/JvmRunner.kt new file mode 100644 index 00000000..c352e298 --- /dev/null +++ b/tests/test-runner/src/jvmMain/kotlin/love/forte/suspendtrans/test/runner/JvmRunner.kt @@ -0,0 +1,20 @@ +package love.forte.suspendtrans.test.runner + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.future.future +import kotlinx.coroutines.runBlocking +import java.util.concurrent.CompletableFuture + +private val scope = CoroutineScope(SupervisorJob()) + +@OptIn(DelicateCoroutinesApi::class) +fun jvmResultToAsync(block: suspend () -> Result): CompletableFuture { + return scope.future { block().getOrThrow() } +} + +@OptIn(DelicateCoroutinesApi::class) +fun jvmResultToBlock(block: suspend () -> Result): T { + return runBlocking { block().getOrThrow() } +} \ No newline at end of file From 83095c09931b4021bfdd9797eae3b86709aec588 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Thu, 10 Jul 2025 14:12:37 +0800 Subject: [PATCH 04/10] refactor: Extract platform-checking logic into `PlatformUtils`, adjust usages across the plugin, and improve transformer initialization with type parameter caching. --- .../fir/SuspendTransformFirTransformer.kt | 130 ++++++++++-------- ...spendTransformSyntheticResolveExtension.kt | 14 +- .../suspendtrans/utils/CopyAnnotationUtils.kt | 4 +- .../suspendtrans/utils/PlatformUtils.kt | 21 +++ 4 files changed, 96 insertions(+), 73 deletions(-) create mode 100644 compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/PlatformUtils.kt diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt index 385207b6..fee84a39 100644 --- a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt @@ -1,15 +1,9 @@ package love.forte.plugin.suspendtrans.fir import love.forte.plugin.suspendtrans.annotation.ExperimentalReturnTypeOverrideGenericApi -import love.forte.plugin.suspendtrans.configuration.MarkAnnotation -import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfiguration -import love.forte.plugin.suspendtrans.configuration.TargetPlatform -import love.forte.plugin.suspendtrans.configuration.Transformer +import love.forte.plugin.suspendtrans.configuration.* import love.forte.plugin.suspendtrans.fqn -import love.forte.plugin.suspendtrans.utils.TransformAnnotationData -import love.forte.plugin.suspendtrans.utils.includeAnnotations -import love.forte.plugin.suspendtrans.utils.toClassId -import love.forte.plugin.suspendtrans.utils.toInfo +import love.forte.plugin.suspendtrans.utils.* import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.fakeElement @@ -55,11 +49,6 @@ import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.buildErrorTypeRef import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef import org.jetbrains.kotlin.name.* -import org.jetbrains.kotlin.platform.isCommon -import org.jetbrains.kotlin.platform.isJs -import org.jetbrains.kotlin.platform.isWasm -import org.jetbrains.kotlin.platform.jvm.isJvm -import org.jetbrains.kotlin.platform.konan.isNative import org.jetbrains.kotlin.types.ConstantValueKind import org.jetbrains.kotlin.utils.keysToMap import java.util.concurrent.ConcurrentHashMap @@ -87,6 +76,11 @@ class SuspendTransformFirTransformer( private val transformerFunctionSymbolMap = ConcurrentHashMap() + private val allMarkAnnotationClassInfos: Set = + suspendTransformConfiguration.transformers.values.flatMapTo(mutableSetOf()) { transformerList -> + transformerList.map { transformer -> transformer.markAnnotation.classInfo } + } + private lateinit var coroutineScopeSymbol: FirClassLikeSymbol<*> private data class FirCacheKey( @@ -98,12 +92,23 @@ class SuspendTransformFirTransformer( val funName: Name, val annotationData: TransformAnnotationData, val transformer: Transformer, - val transformerFunctionSymbol: FirNamedFunctionSymbol, + val transformerFunctionSymbol: FirNamedFunctionSymbol?, val markAnnotationTypeArgument: FirTypeProjection?, - ) + val platform: org.jetbrains.kotlin.platform.TargetPlatform + ) { + fun transformerFunctionSymbol( + transformerFunctionSymbolMap: Map, + finder: (Transformer) -> FirNamedFunctionSymbol? + ): FirNamedFunctionSymbol { + return transformerFunctionSymbol + ?: transformerFunctionSymbolMap[transformer] + ?: finder(transformer) + ?: error("Cannot find transformer function symbol for transformer: $transformer in $platform") + } - private fun initScopeSymbol() { + } + private fun initScopeSymbol() { val classId = ClassId( FqName.fromSegments(listOf("kotlinx", "coroutines")), Name.identifier("CoroutineScope") @@ -114,7 +119,6 @@ class SuspendTransformFirTransformer( ?: session.dependenciesSymbolProvider.getClassLikeSymbolByClassId(classId) ?: error("Cannot resolve `kotlinx.coroutines.CoroutineScope` symbol.") } - } private fun findTransformerFunctionSymbol(transformer: Transformer): FirNamedFunctionSymbol? { @@ -140,8 +144,9 @@ class SuspendTransformFirTransformer( if (transformerFunctionSymbols.isNotEmpty()) { if (transformerFunctionSymbols.size == 1) { - transformerFunctionSymbolMap[transformer] = transformerFunctionSymbols.first() - return transformerFunctionSymbols.first() + val firstFunctionSymbol = transformerFunctionSymbols.first() + transformerFunctionSymbolMap[transformer] = firstFunctionSymbol + return firstFunctionSymbol } else { error("Found multiple transformer function symbols for transformer: $transformer") } @@ -153,14 +158,8 @@ class SuspendTransformFirTransformer( return null } - private fun initTransformerFunctionSymbolMap( - classSymbol: FirClassSymbol<*>, - memberScope: FirClassDeclaredMemberScope? - ): Map { + private fun initTransformerFunctionSymbolMap(): Map { // 尝试找到所有配置的 bridge function, 例如 `runBlocking` 等 - val symbolProvider = session.symbolProvider - val dependenciesSymbolProvider = session.dependenciesSymbolProvider - val map = mutableMapOf() suspendTransformConfiguration.transformers @@ -180,8 +179,7 @@ class SuspendTransformFirTransformer( session.firCachesFactory.createCache { cacheKey, c -> val (symbol, scope) = cacheKey initScopeSymbol() - val transformerFunctionMap = initTransformerFunctionSymbolMap(symbol, scope) - + val transformerFunctionMap = initTransformerFunctionSymbolMap() createCache(symbol, scope, transformerFunctionMap) } @@ -261,9 +259,8 @@ class SuspendTransformFirTransformer( return copyConeType(originalTypeParameterCache) ?: this } - private fun FirSimpleFunctionBuilder.copyParameters() { + private fun FirSimpleFunctionBuilder.copyParameters(originalTypeParameterCache: MutableList) { val newFunSymbol = symbol - val originalTypeParameterCache = mutableListOf() val newTypeParameters = typeParameters.mapToNewTypeParameters(newFunSymbol, originalTypeParameterCache) typeParameters.clear() @@ -358,9 +355,9 @@ class SuspendTransformFirTransformer( thisValueParameters: List, bridgeFunSymbol: FirNamedFunctionSymbol, newFunTarget: FirFunctionTarget, - funData: SyntheticFunData + funData: SyntheticFunData, + originalTypeParameterCache: MutableList ): FirBlock = buildBlock { - val transformer = funData.transformer this.source = originFunc.body?.source // lambda: suspend () -> T @@ -447,7 +444,7 @@ class SuspendTransformFirTransformer( } lambdaTarget.bind(lambda) - val returnType = resolveReturnType(funData, originFunc.returnTypeRef) + val returnType = resolveReturnType(funData, originFunc.returnTypeRef, originalTypeParameterCache) this.statements.add( buildReturnExpression { @@ -593,7 +590,8 @@ class SuspendTransformFirTransformer( funData: SyntheticFunData, results: MutableList, ) { - val realBridgeFunSymbol = funData.transformerFunctionSymbol + val realBridgeFunSymbol = + funData.transformerFunctionSymbol(transformerFunctionSymbolMap, ::findTransformerFunctionSymbol) val annotationData = funData.annotationData if (!annotationData.asProperty) { @@ -623,6 +621,7 @@ class SuspendTransformFirTransformer( // ) // ) val key = SuspendTransformK2V3Key + val originalTypeParameterCache = mutableListOf() val newFunTarget = FirFunctionTarget(null, isLambda = false) val newFun = buildSimpleFunctionCopy(originFunc) { @@ -650,10 +649,10 @@ class SuspendTransformFirTransformer( // In the generated IR, data and dataBlocking will share an `A`, generating the error. // The error: Duplicate IR node // [IR VALIDATION] JvmIrValidationBeforeLoweringPhase: Duplicate IR node: TYPE_PARAMETER name:A index:0 variance: superTypes:[kotlin.Any?] reified:false of FUN GENERATED[...] - copyParameters() + copyParameters(originalTypeParameterCache) // resolve returnType (with wrapped) after copyParameters - returnTypeRef = resolveReturnType(funData, returnTypeRef) + returnTypeRef = resolveReturnType(funData, returnTypeRef, originalTypeParameterCache) val thisReceiverParameter = this.receiverParameter val thisContextParameters = this.contextParameters @@ -672,7 +671,8 @@ class SuspendTransformFirTransformer( thisValueParameters, realBridgeFunSymbol, newFunTarget, - funData + funData, + originalTypeParameterCache ) origin = key.origin @@ -692,6 +692,8 @@ class SuspendTransformFirTransformer( context: MemberGenerationContext? ): List { val owner = context?.owner ?: return emptyList() + val originalTypeParameterCache: MutableList = mutableListOf() + val funcMap = cache.getValue(FirCacheKey(owner, context.declaredScope)) ?.get(callableId.callableName) ?: return emptyList() @@ -734,13 +736,12 @@ class SuspendTransformFirTransformer( val originalReturnType = original.returnTypeRef - val originalTypeParameterCache: MutableList = mutableListOf() val copiedReturnType = originalReturnType.withReplacedConeType( originalReturnType.coneTypeOrNull?.copyConeTypeOrSelf(originalTypeParameterCache) ) // copy完了再resolve,这样里面包的type parameter就不会有问题了(如果有type parameter的话) - val resolvedReturnType = resolveReturnType(funData, copiedReturnType) + val resolvedReturnType = resolveReturnType(funData, copiedReturnType, originalTypeParameterCache) val newFunTarget = FirFunctionTarget(null, isLambda = false) @@ -819,9 +820,13 @@ class SuspendTransformFirTransformer( null, propertyAccessorSymbol, thisValueParameters, - funData.transformerFunctionSymbol, + funData.transformerFunctionSymbol( + transformerFunctionSymbolMap, + ::findTransformerFunctionSymbol + ), newFunTarget, - funData + funData, + originalTypeParameterCache ) }.also { getter -> newFunTarget.bind(getter) @@ -943,14 +948,7 @@ class SuspendTransformFirTransformer( val platform = classSymbol.moduleData.platform fun check(targetPlatform: TargetPlatform): Boolean { - return when { - platform.isJvm() && targetPlatform == TargetPlatform.JVM -> true - platform.isJs() && targetPlatform == TargetPlatform.JS -> true - platform.isWasm() && targetPlatform == TargetPlatform.WASM -> true - platform.isNative() && targetPlatform == TargetPlatform.NATIVE -> true - platform.isCommon() && targetPlatform == TargetPlatform.COMMON -> true - else -> false - } + return checkPlatform(platform, targetPlatform) } // Key -> synthetic fun name @@ -986,7 +984,6 @@ class SuspendTransformFirTransformer( // TODO 也许错误延后抛出,缓存里找不到的话即用即找,可以解决无法在当前模块下使用的问题? // see https://github.com/ForteScarlet/kotlin-suspend-transform-compiler-plugin/issues/100 val transformerFunctionSymbol = transformerFunctionSymbolMap[transformer] - ?: error("Cannot find transformer function symbol for transformer: $transformer in $platform") // 读不到注解的参数? // 必须使用 anno.getXxxArgument(Name(argument name)), @@ -1004,7 +1001,8 @@ class SuspendTransformFirTransformer( annoData, transformer, transformerFunctionSymbol, - markAnnotationTypeArgument + markAnnotationTypeArgument, + platform ) } } @@ -1043,14 +1041,16 @@ class SuspendTransformFirTransformer( private fun resolveReturnType( funData: SyntheticFunData, - returnTypeRef: FirTypeRef + returnTypeRef: FirTypeRef, + originalTypeParameterCache: MutableList ): FirTypeRef { val transformer = funData.transformer val returnTypeArg = funData.markAnnotationTypeArgument val returnTypeConeType = returnTypeArg?.toConeTypeProjection()?.let { // if is star projection, use Any or Nothing? and the nullable? - it.type ?: session.builtinTypes.nothingType.coneType + it.type?.copyWithTypeParameters(originalTypeParameterCache) + ?: session.builtinTypes.nothingType.coneType } ?: returnTypeRef.coneType val resultConeType: ConeKotlinType = resolveReturnConeType(transformer, returnTypeConeType) @@ -1095,18 +1095,24 @@ class SuspendTransformFirTransformer( private fun copyAnnotations( original: FirSimpleFunction, syntheticFunData: SyntheticFunData, ): CopyAnnotations { - // TODO Do not copy marker annotation itself. val transformer = syntheticFunData.transformer val originalAnnotationClassIdMap = original.annotations.keysToMap { it.toAnnotationClassId(session) } val copyFunction = transformer.copyAnnotationsToSyntheticFunction val copyProperty = transformer.copyAnnotationsToSyntheticProperty - val excludes = transformer.copyAnnotationExcludes.map { it.toClassId() } + + val includeClassInfos = transformer.syntheticFunctionIncludeAnnotations.mapTo(mutableSetOf()) { it.classInfo } + // 对于 all mark annotation class infos 来说,它们必须包括在 include class infos 中才能被拷贝 + // 将 all mark annotation class infos 添加到 excludes, 但是排除掉在 include 中包含的。 + val excludeMarkAnnotations = allMarkAnnotationClassInfos - includeClassInfos + + val excludes = + transformer.copyAnnotationExcludes.map { it.toClassId() } + excludeMarkAnnotations.map { it.toClassId() } val includes = transformer.syntheticFunctionIncludeAnnotations.map { it.toInfo() } val markNameProperty = transformer.markAnnotation.markNameProperty - val functionAnnotationList = buildList { + val functionAnnotationList: List = buildList { if (copyFunction) { val notCompileAnnotationsCopied = originalAnnotationClassIdMap.filterNot { (_, annotationClassId) -> if (annotationClassId == null) return@filterNot true @@ -1122,7 +1128,7 @@ class SuspendTransformFirTransformer( * * See https://github.com/ForteScarlet/kotlin-suspend-transform-compiler-plugin/issues/56 */ - val copied = notCompileAnnotationsCopied.map { a -> + val copied: List = notCompileAnnotationsCopied.map { a -> buildAnnotation { annotationTypeRef = buildResolvedTypeRef { coneType = a.resolvedType @@ -1140,7 +1146,13 @@ class SuspendTransformFirTransformer( // add includes includes.forEach { include -> + if (include.classInfo in allMarkAnnotationClassInfos) { + // 如果 classId 在 allMarkAnnotationClassInfos 中,不直接添加 + return@forEach + } + val classId = include.classId + val includeAnnotation = buildAnnotation { argumentMapping = buildAnnotationArgumentMapping() annotationTypeRef = buildResolvedTypeRef { @@ -1179,7 +1191,7 @@ class SuspendTransformFirTransformer( } } - val propertyAnnotationList = buildList { + val propertyAnnotationList: List = buildList { if (copyProperty) { val notCompileAnnotationsCopied = originalAnnotationClassIdMap.filterNot { (_, annotationClassId) -> if (annotationClassId == null) return@filterNot true @@ -1209,7 +1221,7 @@ class SuspendTransformFirTransformer( val infos = transformer.originFunctionIncludeAnnotations.map { it.toInfo() } val includeToOriginals: List = infos - .mapNotNull { (classId, repeatable, _) -> + .mapNotNull { (classId, _, repeatable, _) -> if (!repeatable) { // 不能是已经存在的 if (originalAnnotationClassIdMap.values.any { it == classId }) { diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/symbol/SuspendTransformSyntheticResolveExtension.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/symbol/SuspendTransformSyntheticResolveExtension.kt index b6f7bf20..7387893e 100644 --- a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/symbol/SuspendTransformSyntheticResolveExtension.kt +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/symbol/SuspendTransformSyntheticResolveExtension.kt @@ -15,11 +15,6 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.js.descriptorUtils.getKotlinTypeFqName import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.platform.isCommon -import org.jetbrains.kotlin.platform.isJs -import org.jetbrains.kotlin.platform.isWasm -import org.jetbrains.kotlin.platform.jvm.isJvm -import org.jetbrains.kotlin.platform.konan.isNative import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.constants.ArrayValue import org.jetbrains.kotlin.resolve.constants.ConstantValue @@ -179,14 +174,7 @@ open class SuspendTransformSyntheticResolveExtension(open val configuration: Sus fun check(): Boolean { val platform = classDescriptor.platform - return when { - platform.isJvm() && targetPlatform == TargetPlatform.JVM -> true - platform.isJs() && targetPlatform == TargetPlatform.JS -> true - platform.isWasm() && targetPlatform == TargetPlatform.WASM -> true - platform.isNative() && targetPlatform == TargetPlatform.NATIVE -> true - platform.isCommon() && targetPlatform == TargetPlatform.COMMON -> true - else -> false - } + return checkPlatform(platform, targetPlatform) } if (check()) { diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/CopyAnnotationUtils.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/CopyAnnotationUtils.kt index b04c4ffd..c0e19811 100644 --- a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/CopyAnnotationUtils.kt +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/CopyAnnotationUtils.kt @@ -1,14 +1,16 @@ package love.forte.plugin.suspendtrans.utils +import love.forte.plugin.suspendtrans.configuration.ClassInfo import love.forte.plugin.suspendtrans.configuration.IncludeAnnotation import org.jetbrains.kotlin.name.ClassId data class IncludeAnnotationInfo( val classId: ClassId, + val classInfo: ClassInfo, val repeatable: Boolean, val includeProperty: Boolean, ) fun IncludeAnnotation.toInfo(): IncludeAnnotationInfo { - return IncludeAnnotationInfo(classInfo.toClassId(), repeatable, includeProperty) + return IncludeAnnotationInfo(classInfo.toClassId(), classInfo, repeatable, includeProperty) } diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/PlatformUtils.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/PlatformUtils.kt new file mode 100644 index 00000000..1a9276f5 --- /dev/null +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/PlatformUtils.kt @@ -0,0 +1,21 @@ +package love.forte.plugin.suspendtrans.utils + +import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.isCommon +import org.jetbrains.kotlin.platform.isJs +import org.jetbrains.kotlin.platform.isWasm +import org.jetbrains.kotlin.platform.jvm.isJvm +import org.jetbrains.kotlin.platform.konan.isNative +import love.forte.plugin.suspendtrans.configuration.TargetPlatform as PluginTargetPlatform + +internal fun checkPlatform( + platform: TargetPlatform?, + targetPlatform: PluginTargetPlatform +): Boolean = when { + platform.isJvm() && targetPlatform == PluginTargetPlatform.JVM -> true + platform.isJs() && targetPlatform == PluginTargetPlatform.JS -> true + platform.isWasm() && targetPlatform == PluginTargetPlatform.WASM -> true + platform.isNative() && targetPlatform == PluginTargetPlatform.NATIVE -> true + platform.isCommon() && targetPlatform == PluginTargetPlatform.COMMON -> true + else -> false +} \ No newline at end of file From 837cc818ce009471a390313df39b4540f00d18a9 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Thu, 10 Jul 2025 17:01:43 +0800 Subject: [PATCH 05/10] =?UTF-8?q?=E5=A5=87=E6=80=AA=E3=80=82=E4=B8=BA?= =?UTF-8?q?=E4=BB=80=E4=B9=88=E5=8F=AA=E8=A6=81=E8=A2=AB=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E7=9A=84=E6=B3=A8=E8=A7=A3=EF=BC=8C=E5=AE=83=E5=8E=9F=E6=9C=AC?= =?UTF-8?q?=E7=9A=84=E8=8C=83=E5=9E=8B=E7=B1=BB=E5=9E=8B=E5=B0=B1=E4=BC=9A?= =?UTF-8?q?=E8=A2=AB=E7=A0=B4=E5=9D=8F=E5=91=A2=EF=BC=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fir/SuspendTransformFirTransformer.kt | 54 +++++++++++------- .../plugin/suspendtrans/utils/FirUtils.kt | 6 +- ...spendTransformerEnvironmentConfigurator.kt | 57 ++++++++++++++++++- .../src/testData/codegen/typedAnno.kt | 18 ++++++ .../annotation/JvmBlockingWithType.kt | 23 ++++++++ .../annotation/JvmBlockingWithType.jvm.kt | 21 +++++++ tests/test-jvm/build.gradle.kts | 34 +++++------ .../returntypeoverride/ReturnTypeOverrides.kt | 10 ++-- 8 files changed, 174 insertions(+), 49 deletions(-) create mode 100644 compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt create mode 100644 runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt create mode 100644 runtime/suspend-transform-annotation/src/jvmMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.jvm.kt diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt index fee84a39..66d2d15a 100644 --- a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt @@ -1,6 +1,5 @@ package love.forte.plugin.suspendtrans.fir -import love.forte.plugin.suspendtrans.annotation.ExperimentalReturnTypeOverrideGenericApi import love.forte.plugin.suspendtrans.configuration.* import love.forte.plugin.suspendtrans.fqn import love.forte.plugin.suspendtrans.utils.* @@ -356,7 +355,8 @@ class SuspendTransformFirTransformer( bridgeFunSymbol: FirNamedFunctionSymbol, newFunTarget: FirFunctionTarget, funData: SyntheticFunData, - originalTypeParameterCache: MutableList + originalTypeParameterCache: MutableList, + returnTypeRef: FirTypeRef ): FirBlock = buildBlock { this.source = originFunc.body?.source @@ -444,13 +444,13 @@ class SuspendTransformFirTransformer( } lambdaTarget.bind(lambda) - val returnType = resolveReturnType(funData, originFunc.returnTypeRef, originalTypeParameterCache) + // val returnType = resolveReturnType(funData, originFunc.returnTypeRef, originalTypeParameterCache) this.statements.add( buildReturnExpression { this.target = newFunTarget this.result = buildFunctionCall { - this.coneTypeOrNull = returnType.coneType + this.coneTypeOrNull = returnTypeRef.coneType this.source = originFunc.body?.source this.calleeReference = buildResolvedNamedReference { this.source = bridgeFunSymbol.source @@ -672,7 +672,8 @@ class SuspendTransformFirTransformer( realBridgeFunSymbol, newFunTarget, funData, - originalTypeParameterCache + originalTypeParameterCache, + returnTypeRef ) origin = key.origin @@ -826,7 +827,8 @@ class SuspendTransformFirTransformer( ), newFunTarget, funData, - originalTypeParameterCache + originalTypeParameterCache, + returnTypeRef ) }.also { getter -> newFunTarget.bind(getter) @@ -974,12 +976,16 @@ class SuspendTransformFirTransformer( val anno = firAnnotation(func, markAnnotation, classSymbol) ?: continue - @OptIn(ExperimentalReturnTypeOverrideGenericApi::class) - val markAnnotationTypeArgument = if (markAnnotation.hasReturnTypeOverrideGeneric) { - anno.typeArguments.firstOrNull() - } else { - null - } + val markAnnotationTypeArgument: FirTypeProjection? = null + + // TODO 只要是一个标记注解,范型就会 'ERROR CLASS: Symbol not found for T' + // 为什么? + // @OptIn(ExperimentalReturnTypeOverrideGenericApi::class) + // val markAnnotationTypeArgument: FirTypeProjection? = if (markAnnotation.hasReturnTypeOverrideGeneric) { + // anno.typeArguments.firstOrNull() + // } else { + // null + // } // TODO 也许错误延后抛出,缓存里找不到的话即用即找,可以解决无法在当前模块下使用的问题? // see https://github.com/ForteScarlet/kotlin-suspend-transform-compiler-plugin/issues/100 @@ -1030,14 +1036,16 @@ class SuspendTransformFirTransformer( func: FirNamedFunctionSymbol, markAnnotation: MarkAnnotation, classSymbol: FirBasedSymbol<*>? - ) = func.resolvedAnnotationsWithArguments.getAnnotationsByClassId( - markAnnotation.classId, - session - ).firstOrNull() - ?: classSymbol?.resolvedAnnotationsWithArguments?.getAnnotationsByClassId( + ): FirAnnotation? { + return func.resolvedAnnotationsWithArguments.getAnnotationsByClassId( markAnnotation.classId, session - )?.firstOrNull() + ).firstOrNull() + ?: classSymbol?.resolvedAnnotationsWithArguments?.getAnnotationsByClassId( + markAnnotation.classId, + session + )?.firstOrNull() + } private fun resolveReturnType( funData: SyntheticFunData, @@ -1047,9 +1055,10 @@ class SuspendTransformFirTransformer( val transformer = funData.transformer val returnTypeArg = funData.markAnnotationTypeArgument - val returnTypeConeType = returnTypeArg?.toConeTypeProjection()?.let { + val returnTypeConeType: ConeKotlinType = returnTypeArg?.toConeTypeProjection()?.let { // if is star projection, use Any or Nothing? and the nullable? - it.type?.copyWithTypeParameters(originalTypeParameterCache) + it.type + ?.copyConeTypeOrSelf(originalTypeParameterCache) ?: session.builtinTypes.nothingType.coneType } ?: returnTypeRef.coneType @@ -1093,10 +1102,12 @@ class SuspendTransformFirTransformer( * @return function annotations `to` property annotations. */ private fun copyAnnotations( - original: FirSimpleFunction, syntheticFunData: SyntheticFunData, + original: FirSimpleFunction, + syntheticFunData: SyntheticFunData, ): CopyAnnotations { val transformer = syntheticFunData.transformer + // val originalAnnotationClassIdMap = original.annotations.keysToMap { it.toAnnotationClassId(session) } val originalAnnotationClassIdMap = original.annotations.keysToMap { it.toAnnotationClassId(session) } val copyFunction = transformer.copyAnnotationsToSyntheticFunction @@ -1133,6 +1144,7 @@ class SuspendTransformFirTransformer( annotationTypeRef = buildResolvedTypeRef { coneType = a.resolvedType } + this.typeArguments.addAll(a.typeArguments) this.argumentMapping = buildAnnotationArgumentMapping { this.source = a.source diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/FirUtils.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/FirUtils.kt index 193eea18..ea039d83 100644 --- a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/FirUtils.kt +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/utils/FirUtils.kt @@ -1,8 +1,8 @@ package love.forte.plugin.suspendtrans.utils -import org.jetbrains.kotlin.fir.declarations.FirDeclaration +import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction import org.jetbrains.kotlin.fir.expressions.FirAnnotation -fun FirDeclaration.includeAnnotations(includes: List) { - replaceAnnotations(annotations + includes) +fun FirSimpleFunction.includeAnnotations(includes: List) { + replaceAnnotations(symbol.resolvedAnnotationsWithArguments + includes) } diff --git a/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt b/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt index 30f108c6..378678a3 100644 --- a/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt +++ b/compiler/suspend-transform-plugin/src/test/love/forte/plugin/suspendtrans/services/SuspendTransformerEnvironmentConfigurator.kt @@ -1,12 +1,17 @@ package love.forte.plugin.suspendtrans.services import love.forte.plugin.suspendtrans.SuspendTransformComponentRegistrar -import love.forte.plugin.suspendtrans.configuration.InternalSuspendTransformConfigurationApi -import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfiguration +import love.forte.plugin.suspendtrans.configuration.* import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jsPromiseTransformer +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmApi4JAnnotationClassInfo +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmAsyncMarkAnnotationClassInfo import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmAsyncTransformer +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmBlockingMarkAnnotationClassInfo +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmBlockingTransformFunction import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmBlockingTransformer -import love.forte.plugin.suspendtrans.configuration.TargetPlatform +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmNameAnnotationClassInfo +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.jvmSyntheticClassInfo +import love.forte.plugin.suspendtrans.configuration.SuspendTransformConfigurations.kotlinOptInClassInfo import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar @@ -35,9 +40,45 @@ class SuspendTransformerEnvironmentConfigurator(testServices: TestServices) : En TargetPlatform.JVM to listOf( jvmBlockingTransformer, jvmAsyncTransformer, + + //Test for JvmBlockingWithType + Transformer( + markAnnotation = MarkAnnotation( + classInfo = ClassInfo( + packageName = "love.forte.plugin.suspendtrans.annotation", + className = "JvmBlockingWithType" + ), + defaultSuffix = "Blocking", + markNameProperty = MarkNameProperty( + propertyName = "markName", + annotation = jvmNameAnnotationClassInfo, + annotationMarkNamePropertyName = "name" + ), + hasReturnTypeOverrideGeneric = true + ), + transformFunctionInfo = jvmBlockingTransformFunction, + transformReturnType = null, + transformReturnTypeGeneric = false, + originFunctionIncludeAnnotations = listOf(IncludeAnnotation(jvmSyntheticClassInfo)), + syntheticFunctionIncludeAnnotations = listOf( + IncludeAnnotation( + classInfo = jvmApi4JAnnotationClassInfo, + includeProperty = true + ) + ), + copyAnnotationsToSyntheticFunction = true, + copyAnnotationExcludes = listOf( + jvmSyntheticClassInfo, + jvmBlockingMarkAnnotationClassInfo, + jvmAsyncMarkAnnotationClassInfo, + kotlinOptInClassInfo, + jvmNameAnnotationClassInfo, + ), + ) ) ) ) + // register plugin SuspendTransformComponentRegistrar.register(this, testConfiguration) } @@ -62,6 +103,16 @@ class SuspendTransformerEnvironmentConfigurator(testServices: TestServices) : En it ) } + getRuntimeJarFile("love.forte.plugin.suspendtrans.annotation.JvmBlockingWithType")?.let { + configuration.addJvmClasspathRoot( + it + ) + } + getRuntimeJarFile("love.forte.plugin.suspendtrans.annotation.JvmBlockingWithType0")?.let { + configuration.addJvmClasspathRoot( + it + ) + } // register coroutines getRuntimeJarFile("kotlinx.coroutines.CoroutineScope")?.let { diff --git a/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt b/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt new file mode 100644 index 00000000..06d4e140 --- /dev/null +++ b/compiler/suspend-transform-plugin/src/testData/codegen/typedAnno.kt @@ -0,0 +1,18 @@ +// FIR_DUMP +// DUMP_IR +// SOURCE +// FILE: Main.kt [MainKt#main] + +import love.forte.plugin.suspendtrans.annotation.JvmAsync +import love.forte.plugin.suspendtrans.annotation.JvmBlocking +import love.forte.plugin.suspendtrans.annotation.JvmBlockingWithType +import love.forte.plugin.suspendtrans.annotation.JvmBlockingWithType0 + +annotation class FooAnno + +class Foo { + @JvmBlockingWithType + @JvmBlockingWithType0 + @FooAnno + suspend fun foo(): T = "foo" +} diff --git a/runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt b/runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt new file mode 100644 index 00000000..6d60e182 --- /dev/null +++ b/runtime/suspend-transform-annotation/src/commonMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.kt @@ -0,0 +1,23 @@ +package love.forte.plugin.suspendtrans.annotation + +@OptIn(ExperimentalMultiplatform::class) +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +@OptionalExpectation +public expect annotation class JvmBlockingWithType( + val baseName: String = "", + val suffix: String = "Blocking", + val asProperty: Boolean = false, + val markName: String = "", +) + +@OptIn(ExperimentalMultiplatform::class) +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +@OptionalExpectation +public expect annotation class JvmBlockingWithType0( + val baseName: String = "", + val suffix: String = "Blocking", + val asProperty: Boolean = false, + val markName: String = "", +) diff --git a/runtime/suspend-transform-annotation/src/jvmMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.jvm.kt b/runtime/suspend-transform-annotation/src/jvmMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.jvm.kt new file mode 100644 index 00000000..e92e7d92 --- /dev/null +++ b/runtime/suspend-transform-annotation/src/jvmMain/kotlin/love/forte/plugin/suspendtrans/annotation/JvmBlockingWithType.jvm.kt @@ -0,0 +1,21 @@ +package love.forte.plugin.suspendtrans.annotation + +@OptIn(markerClass = [ExperimentalMultiplatform::class]) +@Target(allowedTargets = [AnnotationTarget.FUNCTION, AnnotationTarget.CLASS]) +@Retention(value = AnnotationRetention.BINARY) +public actual annotation class JvmBlockingWithType actual constructor( + actual val baseName: String, + actual val suffix: String, + actual val asProperty: Boolean, + actual val markName: String +) + +@OptIn(markerClass = [ExperimentalMultiplatform::class]) +@Target(allowedTargets = [AnnotationTarget.FUNCTION, AnnotationTarget.CLASS]) +@Retention(value = AnnotationRetention.BINARY) +public actual annotation class JvmBlockingWithType0 actual constructor( + actual val baseName: String, + actual val suffix: String, + actual val asProperty: Boolean, + actual val markName: String +) \ No newline at end of file diff --git a/tests/test-jvm/build.gradle.kts b/tests/test-jvm/build.gradle.kts index e5b2b3f6..cde36ca9 100644 --- a/tests/test-jvm/build.gradle.kts +++ b/tests/test-jvm/build.gradle.kts @@ -68,23 +68,23 @@ suspendTransformPlugin { transformReturnTypeGeneric = false } // - // addJvmAsync { - // markAnnotation { - // classInfo { - // packageName = "love.forte.suspendtrans.test.runner" - // className = "JvmResultAsync" - // } - // hasReturnTypeOverrideGeneric = true - // } - // transformFunctionInfo { - // packageName = "love.forte.suspendtrans.test.runner" - // functionName = "jvmResultToAsync" - // } - // // CompletableFuture - // // transformReturnType { - // // } - // transformReturnTypeGeneric = true - // } + addJvmAsync { + markAnnotation { + classInfo { + packageName = "love.forte.suspendtrans.test.runner" + className = "JvmResultAsync" + } + hasReturnTypeOverrideGeneric = true + } + transformFunctionInfo { + packageName = "love.forte.suspendtrans.test.runner" + functionName = "jvmResultToAsync" + } + // CompletableFuture + // transformReturnType { + // } + transformReturnTypeGeneric = true + } } } diff --git a/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/returntypeoverride/ReturnTypeOverrides.kt b/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/returntypeoverride/ReturnTypeOverrides.kt index 8ddf6632..e3b78eaf 100644 --- a/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/returntypeoverride/ReturnTypeOverrides.kt +++ b/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/returntypeoverride/ReturnTypeOverrides.kt @@ -4,7 +4,7 @@ import kotlinx.coroutines.delay import love.forte.suspendtrans.test.runner.JvmResultAsync import love.forte.suspendtrans.test.runner.JvmResultBlock -interface Foo { +interface Foo { @JvmResultBlock @JvmResultAsync suspend fun hello(): Result { @@ -15,9 +15,9 @@ interface Foo { // TODO e: file://~/suspend-transform-kotlin-compile-plugin/tests/test-jvm/src/main/kotlin/love/forte/plugin/suspendtrans/sample/returntypeoverride/ReturnTypeOverrides.kt:16:21 Unresolved reference 'T'. // @JvmResultBlock // @JvmResultAsync - // suspend fun foo(value: T): Result { - // delay(1) - // return Result.success(value) - // } + suspend fun foo(value: T): Result { + delay(1) + return Result.success(value) + } } \ No newline at end of file From 816820c8a34733a827a465b4c0304f623d30f8f5 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 11 Jul 2025 13:33:44 +0800 Subject: [PATCH 06/10] refactor: (WIP) Extract internal data classes to separate files for better organization and maintainability within the FIR transformer module. --- .../fir/CopiedTypeParameterPair.kt | 8 ++ .../suspendtrans/fir/CopyAnnotations.kt | 9 ++ .../plugin/suspendtrans/fir/FirCacheKey.kt | 9 ++ .../fir/SuspendTransformFirTransformer.kt | 87 +++---------------- .../suspendtrans/fir/SyntheticFunData.kt | 28 ++++++ 5 files changed, 66 insertions(+), 75 deletions(-) create mode 100644 compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/CopiedTypeParameterPair.kt create mode 100644 compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/CopyAnnotations.kt create mode 100644 compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/FirCacheKey.kt create mode 100644 compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SyntheticFunData.kt diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/CopiedTypeParameterPair.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/CopiedTypeParameterPair.kt new file mode 100644 index 00000000..d17ec02b --- /dev/null +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/CopiedTypeParameterPair.kt @@ -0,0 +1,8 @@ +package love.forte.plugin.suspendtrans.fir + +import org.jetbrains.kotlin.fir.declarations.FirTypeParameter + +internal data class CopiedTypeParameterPair( + val original: FirTypeParameter, + val copied: FirTypeParameter +) \ No newline at end of file diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/CopyAnnotations.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/CopyAnnotations.kt new file mode 100644 index 00000000..7f66d1dd --- /dev/null +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/CopyAnnotations.kt @@ -0,0 +1,9 @@ +package love.forte.plugin.suspendtrans.fir + +import org.jetbrains.kotlin.fir.expressions.FirAnnotation + +internal data class CopyAnnotations( + val functionAnnotations: List, + val propertyAnnotations: List, + val toOriginalAnnotations: List +) \ No newline at end of file diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/FirCacheKey.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/FirCacheKey.kt new file mode 100644 index 00000000..106ec285 --- /dev/null +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/FirCacheKey.kt @@ -0,0 +1,9 @@ +package love.forte.plugin.suspendtrans.fir + +import org.jetbrains.kotlin.fir.scopes.impl.FirClassDeclaredMemberScope +import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol + +internal data class FirCacheKey( + val classSymbol: FirClassSymbol<*>, + val memberScope: FirClassDeclaredMemberScope? +) \ No newline at end of file diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt index 66d2d15a..bd86248f 100644 --- a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SuspendTransformFirTransformer.kt @@ -52,17 +52,6 @@ import org.jetbrains.kotlin.types.ConstantValueKind import org.jetbrains.kotlin.utils.keysToMap import java.util.concurrent.ConcurrentHashMap -private data class CopiedTypeParameterPair( - val original: FirTypeParameter, - val copied: FirTypeParameter -) - -private data class CopyAnnotations( - val functionAnnotations: List, - val propertyAnnotations: List, - val toOriginalAnnotations: List -) - /** * * @author ForteScarlet @@ -82,31 +71,6 @@ class SuspendTransformFirTransformer( private lateinit var coroutineScopeSymbol: FirClassLikeSymbol<*> - private data class FirCacheKey( - val classSymbol: FirClassSymbol<*>, - val memberScope: FirClassDeclaredMemberScope? - ) - - private data class SyntheticFunData( - val funName: Name, - val annotationData: TransformAnnotationData, - val transformer: Transformer, - val transformerFunctionSymbol: FirNamedFunctionSymbol?, - val markAnnotationTypeArgument: FirTypeProjection?, - val platform: org.jetbrains.kotlin.platform.TargetPlatform - ) { - fun transformerFunctionSymbol( - transformerFunctionSymbolMap: Map, - finder: (Transformer) -> FirNamedFunctionSymbol? - ): FirNamedFunctionSymbol { - return transformerFunctionSymbol - ?: transformerFunctionSymbolMap[transformer] - ?: finder(transformer) - ?: error("Cannot find transformer function symbol for transformer: $transformer in $platform") - } - - } - private fun initScopeSymbol() { val classId = ClassId( FqName.fromSegments(listOf("kotlinx", "coroutines")), @@ -182,7 +146,6 @@ class SuspendTransformFirTransformer( createCache(symbol, scope, transformerFunctionMap) } - override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>, context: MemberGenerationContext): Set { val names = mutableSetOf() @@ -346,11 +309,9 @@ class SuspendTransformFirTransformer( originFunc: FirSimpleFunction, originFunSymbol: FirNamedFunctionSymbol, owner: FirClassSymbol<*>, -// thisContextReceivers: MutableList, thisContextParameters: List, thisReceiverParameter: FirReceiverParameter?, newFunSymbol: FirBasedSymbol<*>, -// newFunSymbol: FirNamedFunctionSymbol, thisValueParameters: List, bridgeFunSymbol: FirNamedFunctionSymbol, newFunTarget: FirFunctionTarget, @@ -444,8 +405,6 @@ class SuspendTransformFirTransformer( } lambdaTarget.bind(lambda) - // val returnType = resolveReturnType(funData, originFunc.returnTypeRef, originalTypeParameterCache) - this.statements.add( buildReturnExpression { this.target = newFunTarget @@ -458,14 +417,6 @@ class SuspendTransformFirTransformer( this.resolvedSymbol = bridgeFunSymbol } - // this.dispatchReceiver = buildThisReceiverExpression { - // coneTypeOrNull = originFunSymbol.dispatchReceiverType - // source = originFunSymbol.source - // calleeReference = buildImplicitThisReference { - // boundSymbol = owner - // } - // } - this.argumentList = buildResolvedArgumentList( null, mapping = linkedMapOf().apply { @@ -909,33 +860,16 @@ class SuspendTransformFirTransformer( return isOverride } - private val annotationPredicates = DeclarationPredicate.create { - val annotationFqNames = suspendTransformConfiguration.transformers.values - .flatMapTo(mutableSetOf()) { transformerList -> - transformerList.map { it.markAnnotation.fqName } - } - - hasAnnotated(annotationFqNames) - // var predicate: DeclarationPredicate? = null - // for (value in suspendTransformConfiguration.transformers.values) { - // for (transformer in value) { - // val afq = transformer.markAnnotation.fqName - // predicate = if (predicate == null) { - // annotated(afq) - // } else { - // predicate or annotated(afq) - // } - // } - // } - // - // predicate ?: annotated() - } + private val annotationPredicates + get() = DeclarationPredicate.create { + val annotationFqNames = suspendTransformConfiguration.transformers.values + .flatMapTo(mutableSetOf()) { transformerList -> + transformerList.map { it.markAnnotation.fqName } + } + hasAnnotated(annotationFqNames) + } - /** - * NB: The predict needs to be *registered* in order to parse the [@XSerializable] type - * otherwise, the annotation remains unresolved - */ override fun FirDeclarationPredicateRegistrar.registerPredicates() { register(annotationPredicates) } @@ -958,7 +892,6 @@ class SuspendTransformFirTransformer( // Key: -> origin fun symbol // Values -> FunData val map = ConcurrentHashMap>() -// val transformerFunctionSymbolMap = ConcurrentHashMap() val platformTransformers = suspendTransformConfiguration.transformers .filter { (platform, _) -> check(platform) } @@ -1032,6 +965,9 @@ class SuspendTransformFirTransformer( defaultAsProperty = markAnnotation.defaultAsProperty, ) + /** + * Find mark annotation from the function, and if not found, find from the class. + */ private fun firAnnotation( func: FirNamedFunctionSymbol, markAnnotation: MarkAnnotation, @@ -1544,3 +1480,4 @@ private val FirSimpleFunction.syntheticModifier: Modality? modality == Modality.ABSTRACT -> Modality.OPEN else -> status.modality } + diff --git a/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SyntheticFunData.kt b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SyntheticFunData.kt new file mode 100644 index 00000000..08cbe86a --- /dev/null +++ b/compiler/suspend-transform-plugin/src/main/kotlin/love/forte/plugin/suspendtrans/fir/SyntheticFunData.kt @@ -0,0 +1,28 @@ +package love.forte.plugin.suspendtrans.fir + +import love.forte.plugin.suspendtrans.configuration.Transformer +import love.forte.plugin.suspendtrans.utils.TransformAnnotationData +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.types.FirTypeProjection +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.TargetPlatform + +internal data class SyntheticFunData( + val funName: Name, + val annotationData: TransformAnnotationData, + val transformer: Transformer, + val transformerFunctionSymbol: FirNamedFunctionSymbol?, + val markAnnotationTypeArgument: FirTypeProjection?, + val platform: TargetPlatform +) { + fun transformerFunctionSymbol( + transformerFunctionSymbolMap: Map, + finder: (Transformer) -> FirNamedFunctionSymbol? + ): FirNamedFunctionSymbol { + return transformerFunctionSymbol + ?: transformerFunctionSymbolMap[transformer] + ?: finder(transformer) + ?: error("Cannot find transformer function symbol for transformer: $transformer in $platform") + } + +} \ No newline at end of file From 11c217793fa460f1a447b141d05eb4bdece39f81 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 11 Jul 2025 15:20:34 +0800 Subject: [PATCH 07/10] Gotcha! The first tricky problem is solved. But then a second problem arose: it seemed that there was a problem with using inline value classes when using generics. --- .run/PublishAllToLocal.run.xml | 2 +- buildSrc/src/main/kotlin/IProject.kt | 2 + buildSrc/src/main/kotlin/SigningConfigure.kt | 28 +- .../suspend-transform.dokka-module.gradle.kts | 20 +- ...end-transform.jvm-maven-publish.gradle.kts | 244 +++++++++--------- ...orm.multiplatform-maven-publish.gradle.kts | 158 ++++++------ .../fir/SuspendTransformFirTransformer.kt | 68 ++--- ...spendTransformerEnvironmentConfigurator.kt | 7 +- .../src/testData/codegen/typedAnno.kt | 2 +- .../suspendtrans/runtime/RunInSuspendJvm.kt | 5 + tests/test-jvm/build.gradle.kts | 17 ++ .../returntypeoverride/ReturnTypeOverrides.kt | 23 +- .../suspendtrans/test/runner/Annotations.kt | 9 + .../forte/suspendtrans/test/runner/Res.kt | 7 + .../test/runner/Annotations.jvm.kt | 8 + .../suspendtrans/test/runner/JvmRunner.kt | 5 + 16 files changed, 328 insertions(+), 277 deletions(-) create mode 100644 tests/test-runner/src/commonMain/kotlin/love/forte/suspendtrans/test/runner/Res.kt diff --git a/.run/PublishAllToLocal.run.xml b/.run/PublishAllToLocal.run.xml index aac2a445..edd66b40 100644 --- a/.run/PublishAllToLocal.run.xml +++ b/.run/PublishAllToLocal.run.xml @@ -3,7 +3,7 @@