Skip to content
This repository has been archived by the owner on Oct 8, 2024. It is now read-only.

Commit

Permalink
README.md and something..
Browse files Browse the repository at this point in the history
  • Loading branch information
ForteScarlet committed Jan 1, 2024
1 parent 5290c75 commit 416ade1
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 29 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<Void?>`

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.
Original file line number Diff line number Diff line change
Expand Up @@ -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,

)

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,22 @@ 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
symbol.declaration.declarations
.filterIsInstance<KSFunctionDeclaration>()
.filter { it.isAbstract && Modifier.SUSPEND in it.modifiers }
.forEach { abstractSuspendFunction ->
environment.logger.info("abstractSuspendFunction: $abstractSuspendFunction")
if (asyncTypeBuilder != null) {
val (asyncFun, overriddenFun) = generateAsyncFunctions(
symbol,
Expand All @@ -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() }
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,32 @@ 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(
typeParameterResolver = typeParameterResolver,
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
symbol.declaration.declarations
.filterIsInstance<KSFunctionDeclaration>()
.filter { it.isAbstract && Modifier.SUSPEND in it.modifiers }
.forEach { abstractSuspendFunction ->
environment.logger.info("abstractSuspendFunction: $abstractSuspendFunction")
if (blockingTypeBuilder != null) {
val (blockingFun, overriddenFun) = generateBlockingFunctions(
symbol,
Expand Down Expand Up @@ -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() }
Expand All @@ -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)
Expand Down Expand Up @@ -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() }
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.*
Expand Down Expand Up @@ -180,16 +178,17 @@ internal fun generateReversalTypeSpecBuilder(
symbol: AnnotationAndClassDeclaration,
isEnabled: Boolean,
classNamePrefix: String,
classNameSuffix: String,
): TypeSpec.Builder? {
val declaration = symbol.declaration
val declarationName = declaration.simpleName.asString()

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
Expand Down Expand Up @@ -241,7 +240,8 @@ internal fun generateReversalTypeSpecBuilder(
"""
Generated suspend reversal type for [%N].
@see %N
""".trimIndent(), declarationName, declarationName)
""".trimIndent(), declarationName, declarationName
)


return builder
Expand All @@ -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]
Expand All @@ -276,3 +280,37 @@ internal fun resolveIncludeAnnotations(builder: FunSpec.Builder, source: KSFunct
}

}

/**
* 粗略的判断是否需要标记继承
*/
internal fun shouldOverride(
declaration: KSClassDeclaration,
name: String,
extensionReceiver: KSTypeReference?,
params: List<KSValueParameter>
): 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
}
}
3 changes: 3 additions & 0 deletions samples/simple-sample2/src/commonMain/kotlin/pk1/pk2/Foo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ abstract class S2<T, out R, in I, V : Number>(val name: String) {
@JvmSynthetic
@Throws(Exception::class)
abstract suspend fun run()

open fun runBlocking() {}

@JvmSynthetic
abstract suspend fun get(): String
}

0 comments on commit 416ade1

Please sign in to comment.