diff --git a/README.md b/README.md index 5644225..d65fa14 100644 --- a/README.md +++ b/README.md @@ -156,3 +156,34 @@ interface Foo { > [!note] > If `SuspendReversal.markJvmSynthetic = true`, > then `@JvmSynthetic` must (or is strongly recommended) be added to the suspend function. + + +## Cautions + +1. The processor will only handle the **abstract suspend** function. + +2. In Java, functions with a return value of `Unit` are treated as `CompletableFuture` + +3. Will copy the annotations `@kotlin.Throws` and `@kotlin.jvm.Throws`. + +4. Will **roughly** calculate whether the generated function needs to be inherited or not. + +e.g. +```kotlin +interface Foo { + suspend fun run() + fun runBlocking() { /*...*/ } +} + +// Generated 👇 + +interface JBlockingFoo : Foo { + override fun runBlocking() // Will be marked `override` + suspend fun run() { + runBlocking() + } +} +``` + +> [!warning] +> The judgment is very rough and not very reliable. diff --git a/annotations/src/commonMain/kotlin/love/forte/suspendreversal/annotations/SuspendReversal.kt b/annotations/src/commonMain/kotlin/love/forte/suspendreversal/annotations/SuspendReversal.kt index 030c5c8..2ee4faa 100644 --- a/annotations/src/commonMain/kotlin/love/forte/suspendreversal/annotations/SuspendReversal.kt +++ b/annotations/src/commonMain/kotlin/love/forte/suspendreversal/annotations/SuspendReversal.kt @@ -7,9 +7,69 @@ package love.forte.suspendreversal.annotations @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.SOURCE) public annotation class SuspendReversal( - val javaBlocking: Boolean = true, - val javaAsync: Boolean = true, + val jBlocking: Boolean = true, + val jBlockingClassNamePrefix: String = "JBlocking", + val jBlockingClassNameSuffix: String = "", + val jAsync: Boolean = true, + val jAsyncClassNamePrefix: String = "JAsync", + val jAsyncClassNameSuffix: String = "", val jsAsync: Boolean = true, + val jsAsyncClassNamePrefix: String = "JsAsync", + val jsAsyncClassNameSuffix: String = "", val markJvmSynthetic: Boolean = true, // todo more options..? -) +) { + + // TODO + @Target(AnnotationTarget.FUNCTION) + @Retention(AnnotationRetention.SOURCE) + public annotation class JBlocking( + /** + * 生成函数的基础名称,如果为空则为当前函数名。 + * 最终生成的函数名为 [baseName] + [suffix]。 + */ + val baseName: String = "", + + /** + * [baseName] 名称基础上追加的名称后缀。 + */ + val suffix: String = "Blocking", + val asProperty: Boolean = false, + ) + + // TODO + @Target(AnnotationTarget.FUNCTION) + @Retention(AnnotationRetention.SOURCE) + public annotation class JAsync( + /** + * 生成函数的基础名称,如果为空则为当前函数名。 + * 最终生成的函数名为 [baseName] + [suffix]。 + */ + val baseName: String = "", + + /** + * [baseName] 名称基础上追加的名称后缀。 + */ + val suffix: String = "Async", + val asProperty: Boolean = false, + ) + + // TODO + @Target(AnnotationTarget.FUNCTION) + @Retention(AnnotationRetention.SOURCE) + public annotation class JsAsync( + /** + * 生成函数的基础名称,如果为空则为当前函数名。 + * 最终生成的函数名为 [baseName] + [suffix]。 + */ + val baseName: String = "", + + /** + * [baseName] 名称基础上追加的名称后缀。 + */ + val suffix: String = "Async", + val asProperty: Boolean = false, + + ) + +} diff --git a/processor/src/main/kotlin/love/forte/suspendreversal/processor/ProcessJs.kt b/processor/src/main/kotlin/love/forte/suspendreversal/processor/ProcessJs.kt index e65d012..2a82131 100644 --- a/processor/src/main/kotlin/love/forte/suspendreversal/processor/ProcessJs.kt +++ b/processor/src/main/kotlin/love/forte/suspendreversal/processor/ProcessJs.kt @@ -19,13 +19,15 @@ internal fun resolveJs( typeParameterResolver: TypeParameterResolver, superClassName: TypeName, ) { + val nearestAnnotation = symbol.nearestAnnotation val asyncTypeBuilder: TypeSpec.Builder? = generateReversalTypeSpecBuilder( typeParameterResolver = typeParameterResolver, superClassName = superClassName, environment = environment, symbol = symbol, - isEnabled = symbol.nearestAnnotation.jsAsync, - classNamePrefix = "JsAsync" + isEnabled = nearestAnnotation.jsAsync, + classNamePrefix = nearestAnnotation.jsAsyncClassNamePrefix, + classNameSuffix = nearestAnnotation.jsAsyncClassNameSuffix ) // all suspend abstract fun @@ -33,7 +35,6 @@ internal fun resolveJs( .filterIsInstance() .filter { it.isAbstract && Modifier.SUSPEND in it.modifiers } .forEach { abstractSuspendFunction -> - environment.logger.info("abstractSuspendFunction: $abstractSuspendFunction") if (asyncTypeBuilder != null) { val (asyncFun, overriddenFun) = generateAsyncFunctions( symbol, @@ -53,6 +54,9 @@ private fun generateAsyncFunctions( typeParameterResolver: TypeParameterResolver, abstractSuspendFunction: KSFunctionDeclaration ): GeneratedReversalFunctions { + val abstractSuspendFunctionName = abstractSuspendFunction.simpleName.asString() + val funName = abstractSuspendFunctionName + "Async" + val funcParameterResolver = abstractSuspendFunction.typeParameters.toTypeParameterResolver(typeParameterResolver) val modifiers = abstractSuspendFunction.modifiers.mapNotNull { it.toKModifier() } @@ -71,14 +75,24 @@ private fun generateAsyncFunctions( ).build() } - val abstractSuspendFunctionName = abstractSuspendFunction.simpleName.asString() - val abstractAsyncFunction = FunSpec.builder(abstractSuspendFunctionName + "Async").apply { + val override = shouldOverride( + annotationInfo.declaration, + funName, + abstractSuspendFunction.extensionReceiver, + abstractSuspendFunction.parameters + ) + + + val abstractAsyncFunction = FunSpec.builder(funName).apply { // doc addKdoc("Async reversal function for [%N]", abstractSuspendFunctionName) val modifiers0 = modifiers.toMutableSet() modifiers0.remove(KModifier.SUSPEND) modifiers0.add(KModifier.ABSTRACT) + if (override) { + modifiers0.add(KModifier.OVERRIDE) + } addModifiers(modifiers0) addTypeVariables(typeVariables) diff --git a/processor/src/main/kotlin/love/forte/suspendreversal/processor/ProcessJvm.kt b/processor/src/main/kotlin/love/forte/suspendreversal/processor/ProcessJvm.kt index d3b4e63..5ea5709 100644 --- a/processor/src/main/kotlin/love/forte/suspendreversal/processor/ProcessJvm.kt +++ b/processor/src/main/kotlin/love/forte/suspendreversal/processor/ProcessJvm.kt @@ -19,13 +19,15 @@ internal fun resolveJvm( typeParameterResolver: TypeParameterResolver, superClassName: TypeName, ) { + val nearestAnnotation = symbol.nearestAnnotation val blockingTypeBuilder: TypeSpec.Builder? = generateReversalTypeSpecBuilder( typeParameterResolver = typeParameterResolver, superClassName = superClassName, environment = environment, symbol = symbol, - isEnabled = symbol.nearestAnnotation.javaBlocking, - classNamePrefix = "JBlocking" + isEnabled = nearestAnnotation.jBlocking, + classNamePrefix = nearestAnnotation.jBlockingClassNamePrefix, + classNameSuffix = nearestAnnotation.jBlockingClassNameSuffix ) val asyncTypeBuilder: TypeSpec.Builder? = generateReversalTypeSpecBuilder( @@ -33,8 +35,9 @@ internal fun resolveJvm( superClassName = superClassName, environment = environment, symbol = symbol, - isEnabled = symbol.nearestAnnotation.javaAsync, - classNamePrefix = "JAsync" + isEnabled = nearestAnnotation.jAsync, + classNamePrefix = nearestAnnotation.jAsyncClassNamePrefix, + classNameSuffix = nearestAnnotation.jAsyncClassNameSuffix, ) // all suspend abstract fun @@ -42,7 +45,6 @@ internal fun resolveJvm( .filterIsInstance() .filter { it.isAbstract && Modifier.SUSPEND in it.modifiers } .forEach { abstractSuspendFunction -> - environment.logger.info("abstractSuspendFunction: $abstractSuspendFunction") if (blockingTypeBuilder != null) { val (blockingFun, overriddenFun) = generateBlockingFunctions( symbol, @@ -74,6 +76,8 @@ private fun generateBlockingFunctions( typeParameterResolver: TypeParameterResolver, abstractSuspendFunction: KSFunctionDeclaration ): GeneratedReversalFunctions { + val abstractSuspendFunctionName = abstractSuspendFunction.simpleName.asString() + val funName = abstractSuspendFunctionName + "Blocking" // TODO from annotation val funcParameterResolver = abstractSuspendFunction.typeParameters.toTypeParameterResolver(typeParameterResolver) val modifiers = abstractSuspendFunction.modifiers.mapNotNull { it.toKModifier() } @@ -93,14 +97,27 @@ private fun generateBlockingFunctions( ).build() } - val abstractSuspendFunctionName = abstractSuspendFunction.simpleName.asString() - val abstractBlockingFunction = FunSpec.builder(abstractSuspendFunctionName + "Blocking").apply { + val override = shouldOverride( + annotationInfo.declaration, + funName, + abstractSuspendFunction.extensionReceiver, + abstractSuspendFunction.parameters + ) + + val abstractBlockingFunction = FunSpec.builder(funName).apply { // doc - addKdoc("Blocking reversal function for [%N]\n\n @see %N", abstractSuspendFunctionName, abstractSuspendFunctionName) + addKdoc( + "Blocking reversal function for [%N]\n\n @see %N", + abstractSuspendFunctionName, + abstractSuspendFunctionName + ) val modifiers0 = modifiers.toMutableSet() modifiers0.remove(KModifier.SUSPEND) modifiers0.add(KModifier.ABSTRACT) + if (override) { + modifiers0.add(KModifier.OVERRIDE) + } addModifiers(modifiers0) addTypeVariables(typeVariables) @@ -160,6 +177,8 @@ private fun generateAsyncFunctions( typeParameterResolver: TypeParameterResolver, abstractSuspendFunction: KSFunctionDeclaration ): GeneratedReversalFunctions { + val abstractSuspendFunctionName = abstractSuspendFunction.simpleName.asString() + val funName = abstractSuspendFunctionName + "Async" val funcParameterResolver = abstractSuspendFunction.typeParameters.toTypeParameterResolver(typeParameterResolver) val modifiers = abstractSuspendFunction.modifiers.mapNotNull { it.toKModifier() } @@ -177,16 +196,27 @@ private fun generateAsyncFunctions( modifiers = modifiers0 ).build() } + val override = shouldOverride( + annotationInfo.declaration, + funName, + abstractSuspendFunction.extensionReceiver, + abstractSuspendFunction.parameters + ) - val abstractSuspendFunctionName = abstractSuspendFunction.simpleName.asString() - val abstractAsyncFunction = FunSpec.builder(abstractSuspendFunctionName + "Async").apply { + val abstractAsyncFunction = FunSpec.builder(funName).apply { // doc - addKdoc("Async reversal function for [%N]\n\n @see %N", abstractSuspendFunctionName, abstractSuspendFunctionName) + addKdoc( + "Async reversal function for [%N]\n\n @see %N", + abstractSuspendFunctionName, + abstractSuspendFunctionName + ) val modifiers0 = modifiers.toMutableSet() modifiers0.remove(KModifier.SUSPEND) modifiers0.add(KModifier.ABSTRACT) - + if (override) { + modifiers0.add(KModifier.OVERRIDE) + } addModifiers(modifiers0) addTypeVariables(typeVariables) diff --git a/processor/src/main/kotlin/love/forte/suspendreversal/processor/SuspendInterfaceReversalProcessor.kt b/processor/src/main/kotlin/love/forte/suspendreversal/processor/SuspendInterfaceReversalProcessor.kt index f143156..d8878d6 100644 --- a/processor/src/main/kotlin/love/forte/suspendreversal/processor/SuspendInterfaceReversalProcessor.kt +++ b/processor/src/main/kotlin/love/forte/suspendreversal/processor/SuspendInterfaceReversalProcessor.kt @@ -1,8 +1,6 @@ package love.forte.suspendreversal.processor -import com.google.devtools.ksp.KspExperimental -import com.google.devtools.ksp.getAnnotationsByType -import com.google.devtools.ksp.isAbstract +import com.google.devtools.ksp.* import com.google.devtools.ksp.processing.* import com.google.devtools.ksp.symbol.* import com.squareup.kotlinpoet.* @@ -180,6 +178,7 @@ internal fun generateReversalTypeSpecBuilder( symbol: AnnotationAndClassDeclaration, isEnabled: Boolean, classNamePrefix: String, + classNameSuffix: String, ): TypeSpec.Builder? { val declaration = symbol.declaration val declarationName = declaration.simpleName.asString() @@ -187,9 +186,9 @@ internal fun generateReversalTypeSpecBuilder( val builder: TypeSpec.Builder = if (isEnabled) { when (declaration.classKind) { ClassKind.CLASS -> { - environment.logger.info("CLASS declaration: $declaration") + val className = classNamePrefix + declaration.simpleName.asString() + classNameSuffix - TypeSpec.classBuilder(classNamePrefix + declaration.simpleName.asString()) + TypeSpec.classBuilder(className) .apply { superclass(superClassName) // build primary constructor @@ -241,7 +240,8 @@ internal fun generateReversalTypeSpecBuilder( """ Generated suspend reversal type for [%N]. @see %N - """.trimIndent(), declarationName, declarationName) + """.trimIndent(), declarationName, declarationName + ) return builder @@ -252,14 +252,18 @@ internal data class GeneratedReversalFunctions( val overriddenSuspendFunction: FunSpec ) -internal fun KSType.resolveClassDeclarationToClassName(): ClassName? { +internal fun KSType.resolveClassDeclaration(): KSClassDeclaration? { return when (val declaration = declaration) { - is KSClassDeclaration -> declaration.toClassName() - is KSTypeAlias -> declaration.type.resolve().resolveClassDeclarationToClassName() + is KSClassDeclaration -> declaration + is KSTypeAlias -> declaration.findActualType() else -> return null } } +internal fun KSType.resolveClassDeclarationToClassName(): ClassName? { + return resolveClassDeclaration()?.toClassName() +} + /** * 需要添加(拷贝)的注解们 * - [JvmThrowsClassName] @@ -276,3 +280,37 @@ internal fun resolveIncludeAnnotations(builder: FunSpec.Builder, source: KSFunct } } + +/** + * 粗略的判断是否需要标记继承 + */ +internal fun shouldOverride( + declaration: KSClassDeclaration, + name: String, + extensionReceiver: KSTypeReference?, + params: List +): Boolean { + return declaration.getAllFunctions().any { func -> + if (func.simpleName.asString() != name) { + return@any false + } + + val funcReceiver = func.extensionReceiver + if (funcReceiver != extensionReceiver) return@any false + val funParams = func.parameters + if (funParams.size != params.size) return@any false + + for ((index, funParam) in funParams.withIndex()) { + val param = params[index] + if (param.type == funParam.type) continue + + val funParamClassDecl = funParam.type.resolve().resolveClassDeclaration() + val paramClassDecl = param.type.resolve().resolveClassDeclaration() + + // skip. + if (funParamClassDecl == null || paramClassDecl == null) return false + } + + return true + } +} diff --git a/samples/simple-sample2/src/commonMain/kotlin/pk1/pk2/Foo.kt b/samples/simple-sample2/src/commonMain/kotlin/pk1/pk2/Foo.kt index 580b31c..931eb69 100644 --- a/samples/simple-sample2/src/commonMain/kotlin/pk1/pk2/Foo.kt +++ b/samples/simple-sample2/src/commonMain/kotlin/pk1/pk2/Foo.kt @@ -43,6 +43,9 @@ abstract class S2(val name: String) { @JvmSynthetic @Throws(Exception::class) abstract suspend fun run() + + open fun runBlocking() {} + @JvmSynthetic abstract suspend fun get(): String }