Llonvne / KRequest

a Declarative HTTP client/Kotlin Symbol Processor using KSP and OkHttp3
1 stars 0 forks source link

using Kotlin compiler plugin to generate *create* function implemention #1

Open Llonvne opened 1 year ago

Llonvne commented 1 year ago
inline fun <reified Type> createAPI(baseUrl: String, okHttpClient: OkHttpClient): Type =
    Class.forName(Type::class.qualifiedName + "Impl")
        .constructors.first().newInstance(baseUrl, okHttpClient) as Type

KRequest is now using the create function based on the reflection API, which will lead to unnecessary overhead. Consider replacing it with the generated corresponding implementation class directly using the Kotlin compiler plugin. KRequest has the following convention for generated classes: '{{Your Api Interface name}}Impl'. So it can be implemented in the following way.

object KRequest {
    fun <T> create(baseUrl: String, okHttpClient: OkHttpClient): T {
        throw NotImplementedError("This implementation will be substituted by the compiler plugin")
    }
}

This is a KRequest object that includes a create function. The function accepts two parameters, baseUrl and OkHttpClient. The call to this function will be overridden by the Kotlin compiler plugin to directly call the corresponding implementation class's constructor.

class KRequestIrGenerator : IrGenerationExtension {
    override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
        moduleFragment.transformChildrenVoid(object : IrElementTransformerVoid() {
            override fun visitCall(expression: IrCall): IrExpression {
                if (expression.symbol.owner.name.asString() == "create" &&
                    expression.dispatchReceiver?.type?.classFqName?.asString() == "KRequest"

                ) {
                    val apiType = expression.typeArguments[0]?.classFqName!!
                    val apiImplName = "${apiType.asString()}Impl"
                    val apiClassImplSymbol =
                        pluginContext.referenceClass(ClassId(FqName.ROOT, Name.identifier(apiImplName)))
                    val constructorSymbol = apiClassImplSymbol?.constructors?.toList()
                        ?.get(0)!!
                    val constructorCall = IrConstructorCallImpl.fromSymbolOwner(
                        expression.startOffset, expression.endOffset,
                        constructorSymbol.owner.returnType,
                        constructorSymbol
                    )
                    constructorCall.putValueArgument(0, expression.getValueArgument(0))
                    constructorCall.putValueArgument(1, expression.getValueArgument(1))
                    return constructorCall
                }
                return super.visitCall(expression)
            }
        })
    }
}

The above IR Transformer will inspect the create method calls on KRequest objects. Whenever a match is found, it will replace this call with a direct constructor call to eliminate the overhead incurred by the previous use of reflection.

Llonvne commented 1 year ago

Here are a few reasons why the plugin has not been launched:

Llonvne commented 1 year ago

Due to BennyHuo's Kotlin Compiler Plugin Template, a KCP version of KRequest will be available soon!