Sword - 为 Kotlin 函数增长署理功能(四) - Kotlin IR

手机游戏开发者 2024-9-3 11:30:43 93 0 来自 中国
简介

Sword:一个可以给 Kotlin 函数增长署理的第三方库,基于 KCP 实现。

  • Sword - 为 Kotlin 函数增长署理功能(一)
  • Sword - 为 Kotlin 函数增长署理功能(二)
  • Sword - 为 Kotlin 函数增长署理功能(三)
前面三篇文章笔者记录了 Sword 的实现过程,怎样利用 Sword 以及怎样通过 KSP 为 InvocationHandler 天生 FqName 索引类 HandlerFqName。
在第三篇文章的末了笔者有一个新的想法:通过 Kotlin IR 重新实现 Sword 的功能。颠末近来几天晚上和朝晨的积极,笔者开端实现了 Sword 的功能,大概尚有一些题目,但是效果已经到达了笔者的预期,遂本篇文章记录下笔者的实现过程。
Kotlin IR 是什么以及可以做什么,本文不再赘述,网上有不少资料,读者可以自行参考。
预期效果

假设有以下类(GetTextNoArgInvocationHandler)和函数(getTextNoArg()):
@ProxyHandler("GET_TEXT_NO_ARG") class GetTextNoArgInvocationHandler : InvocationHandler {      private val TAG = GetTextNoArgInvocationHandler::class.java.simpleName      override fun invoke(className: String, methodName: String, args: Array<Any?>): Any? {         Log.e(TAG, "invoke: className = $className, methodName = $methodName, args(${args.size}) = ${args.joinToString()}")         return "guodongAndroid-Debug"     } }  // -----------------------------------------------------------------------------------------------------------------------  @Proxy(     enable = true,     handler = HandlerFqName.GET_TEXT_NO_ARG ) fun getTextNoArg() = "guodongAndroid"通过 Kotlin IR 编译和 Sword 署理后,笔者渴望 getTextNoArg 函数转换成雷同下面的伪代码:
@Proxy(     enable = true,     handler = HandlerFqName.GET_TEXT_NO_ARG ) fun getTextNoArg(): String {     return GetTextNoArgInvocationHandler().invoke("Test", "getTextNoArg", emptyArray()) as String }SwordComponentRegistrar

要利用 IR 起首须要注册 IrGenerationExtension 扩展,修改之前 SwordComponentRegistrar 中的代码:
class SwordComponentRegistrar : ComponentRegistrar {     override fun registerProjectComponents(         project: MockProject,         configuration: CompilerConfiguration     ) {         val messageCollector =             configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)          /*ClassBuilderInterceptorExtension.registerExtension(             project,             SwordClassGenerationInterceptor(messageCollector)         )*/          IrGenerationExtension.registerExtension(             project,             SwordIrGenerationExtension(messageCollector)         )     } }在上面的代码中:

  • 解释掉之前通过 ASM 修改字节码的 ClassBuilderInterceptorExtension 扩展,
  • 新增 IrGenerationExtension 扩展。
Dump

在 IR 语法树中,所有的节点都实现了 IrElement 接口,这些节点可以是模块,包,文件,类,属性,函数,参数,表达式,函数调用、函数体等等。
那么这些节点是什么样子的呢?实现我们的 SwordIrGenerationExtension:
class SwordIrGenerationExtension(     private val messageCollector: MessageCollector, ) : IrGenerationExtension {     override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {         messageCollector.report(             CompilerMessageSeverity.WARNING,             moduleFragment.dump()         )     } }利用 IrElement 的扩展函数 dump 可以输出这些节点的语法树信息。
在上面的代码中,我们输出了整个模块节点的语法树信息,假如模块中有很多文件,类,那么这些信息是相当巨大的,Sword 的目的是 Kotlin 函数,以是此处笔者不再贴出模块节点的语法树信息,比及我们转换函数时,再看看函数节点的语法树信息。
假设我们不知道怎样编写 IR 编译器插件的代码,我们可以先写出要实现效果的 Kotlin 代码,再借助 dump 函数输出 IR 语法树信息,参考且对比语法树信息举行开发 IR 编译器插件,以是 dump 在开发 IR 编译器插件时非常有用。
IrElement

前面说了很多 IrElement 接口,目前我们还不知道它的真面貌,接下来让我们看看它吧:
interface IrElement {     ...      fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R     fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D)      fun <D> transform(transformer: IrElementTransformer<D>, data: D): IrElement     fun <D> transformChildren(transformer: IrElementTransformer<D>, data: D) }在 IrElement 中有四个接口函数,此中 accept 函数基于访问者模式访问各个节点,transform 函数又是基于 accept 函数提供修改节点语法树的能力。
accept 函数中的参数 IrElementVisitor 接口提供了访问各个节点的函数:
interface IrElementVisitor<out R, in D> {     fun visitElement(element: IrElement, data: D): R     fun visitModuleFragment(declaration: IrModuleFragment, data: D) = visitElement(declaration, data)     fun visitPackageFragment(declaration: IrPackageFragment, data: D) = visitElement(declaration, data)     fun visitFile(declaration: IrFile, data: D) = visitPackageFragment(declaration, data)     fun visitDeclaration(declaration: IrDeclarationBase, data: D) = visitElement(declaration, data)     fun visitClass(declaration: IrClass, data: D) = visitDeclaration(declaration, data)     fun visitFunction(declaration: IrFunction, data: D) = visitDeclaration(declaration, data)     ... }IrElementVisitor 中有非常多接口函数,上面代码片断枚举了一些接口函数,这些接口函数大多数都有默认实现,细致观察这些函数的默认实现,末了都直接或间接的调用到 visitElement 函数。
transform 函数中的参数 IrElementTransformer 接口继承自 IrElementVisitor 接口,IrElementTransformer 接口重要是实现了 IrElementVisitor 的接口函数并调用 IrElement.transformChildren 函数遍历节点修改节点的语法树:
interface IrElementTransformer<in D> : IrElementVisitor<IrElement, D> {     override fun visitElement(element: IrElement, data: D): IrElement {         element.transformChildren(this, data)         return element     }      override fun visitModuleFragment(declaration: IrModuleFragment, data: D): IrModuleFragment {         declaration.transformChildren(this, data)         return declaration     }      override fun visitFile(declaration: IrFile, data: D): IrFile {         declaration.transformChildren(this, data)         return declaration     } }在 Sword 中,我们重要利用 transform 函数修改节点语法树的能力来实现为 Kotlin 函数增长署理功能。
IrType & IrSymbol

在 IR 中不但有 IrElement 尚有 IrType 和 IrSymbol 。那么这两个有什么作用呢?
IrType

IrType 可以说是 KotlinType 在 IR 中的另一种体现情势,体现 Kotlin 中的各种范例,好比 Any,Boolean,Int,String 等等。IrType 常用在比力函数参数范例,调用函数时传入参数范例等,举个 Sword 中的栗子:
private val anyNType = pluginContext.irBuiltIns.anyNType private val stringType = pluginContext.irBuiltIns.stringType private val arrayAnyNType = pluginContext.irBuiltIns.arrayClass.typeWith(anyNType)  // `InvocationHandler.invoke(className: String, methodName: String, args: Array<Any?>): Any?` val invokeSymbol =     pluginContext.referenceFunctions(FqName("${param.handler}.$INVOKE_METHOD_NAME"))         .single {             val valueParameters = it.owner.valueParameters             valueParameters.size == 3 &&             valueParameters[0].type == stringType &&             valueParameters[1].type == stringType &&             valueParameters[2].type == arrayAnyNType         }在上面的代码片断中 pluginContext 是 IrGenerationExtension.generate 函数中的第二个参数,通过它的 irBuiltIns 字段我们获取一些 Kotlin 内置的 IrType。

  • anyNType 体现 Kotlin 中的 Any? 范例,
  • stringType 体现 String 范例,假如想获取 String? 范例,则须要调用 stringType.makeNullable() ,
  • arrayAnyNType 体现 Array<Any?> 范例,Array 没有对应的 IrType 体现,我们须要先获取 Array 对应的 IrSymbol,再调用 typeWith 扩展函数传入所需泛型的 IrType 即可获取 Array<Any?> 的 IrType。
在上面代码片断的末了,笔者的目的是获取 InvocationHandler.invoke 函数的 IrSymbol,思量到开发者大概会重载 invoke 函数,以是笔者增长了以下判定逻辑:

  • 函数中有且仅有三个参数,
  • 第一个参数的范例必须是 String 范例,
  • 第二个参数的范例必须是 String 范例,
  • 第三个参数的范例必须是 Array<Any?> 范例。
满足以上几个条件的函数笔者才以为是 InvocationHandler.invoke 函数。
IrSymbol

IrSymbol 在 Kotlin IR 中可以算是比力告急的一个接口了。它以「符号」的情势形貌了 Kotlin 的包、文件、类、函数、属性、字段等,笔者把它明白为 Java 字节码中的形貌符,以是 IrSymbol 常用在创建类、函数、属性,函数调用等,举个 Sword 中的栗子:
private val anyNType = pluginContext.irBuiltIns.anyNType private val emptyArraySymbol = pluginContext.referenceFunctions(FqName("kotlin.emptyArray")).first()  irCall(emptyArraySymbol).also {     it.putTypeArgument(0, anyNType) }

  • anyNType 在上节中出现过,它体现 Any? 范例,
  • emptyArraySymbol 是 emptyArray() 函数在 IR 中的符号,我们同样可以通过 irBuiltIns 获取一些 Kotlin 内置的 IrSymbol,其他的符号可以通过 pluginContext.referenceXXX() 的一系列函数查找
  • 接下来调用 irCall 函数并传入 emptyArraySymbol,末了调用 putTypeArgument() 函数设置 emptyArray() 函数的泛型。
以是上面的代码片断着实是调用 Kotlin 中的 emptyArray<Any?>() 函数。
Sword

SwordIrGenerationExtension

class SwordIrGenerationExtension(     private val messageCollector: MessageCollector, ) : IrGenerationExtension {      private val proxyAnnotationFqName = FqName("com.guodong.android.sword.api.kt.Proxy")      override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {         // 输出日记         messageCollector.report(             CompilerMessageSeverity.WARNING,             "Welcome to guodongAndroid sword kcp kotlin ir plugin"         )          // 判定当前模块是否存在 `Proxy` 注解         val proxyAnnotation = pluginContext.referenceClass(proxyAnnotationFqName)         if (proxyAnnotation == null) {             messageCollector.report(                 CompilerMessageSeverity.ERROR,                 "Not found `Proxy` annotation, make sure to add the "sword-api-kt" library to your dependencies"             )             return         }          // 开始转换         moduleFragment.transform(             SwordTransformer(pluginContext, proxyAnnotation, messageCollector),             null         )     } }颠末前面的知识铺垫, SwordIrGenerationExtension 中的代码逻辑信赖读者应该能明白了,笔者这里就不再赘述了,没有明白的读者可以再回首下前面的内容。
接下来我们重要看看 SwordTransformer 中的逻辑。
SwordTransformer

由于 Sword 的功能是为 Kotlin 函数增长署理功能,以是在 SwordTransformer 中我们仅关注与函数相干的转换函数,即:visitFunctionNew(declaration: IrFunction) 函数。
起首声明一些变量和常量,此中一些变量前面笔者已经先容过:
companion object {     // `InvocationHandler` 中的 `invoke` 函数名称     private const val INVOKE_METHOD_NAME = "invoke" }  private val anyNType = pluginContext.irBuiltIns.anyNType private val stringType = pluginContext.irBuiltIns.stringType private val arrayAnyNType = pluginContext.irBuiltIns.arrayClass.typeWith(anyNType) private val emptyArraySymbol = pluginContext.referenceFunctions(FqName("kotlin.emptyArray")).first() private val arrayOfSymbol = pluginContext.irBuiltIns.arrayOf  // @JvmName 注解完全限定名 private val jvmNameAnnotationFqName = FqName("kotlin.jvm.JvmName")  // @Proxy 注解完全限定名 private val proxyAnnotationFqName = FqName("com.guodong.android.sword.api.kt.Proxy")下面我们就覆写 visitFunctionNew() 函数,在覆写的 visitFunctionNew() 函数中有较多的代码逻辑,照旧老风俗,笔者先形貌下本身的实现思绪,然后再根据实现思绪依次举行代码实现:

  • 过滤一些笔者以为不须要处理处罚的函数,读者可以自行斟酌;过滤不包罗 Proxy 注解的函数,
  • 获取 Proxy 注解里的数据存储于第二篇文章中的 SwordParam 中,
  • 获取当前类名和函数名,判定当前函数是否启用署理,假如启用了署理但是 handler 为空则抛出非常,
  • 启用署理后,我们直接扬弃原函数体,天生新的署理函数体。
1.过滤

override fun visitFunctionNew(declaration: IrFunction): IrStatement {     // 过滤挂起函数,内联函数,多平台声明函数,外部函数(JNI)     if (declaration.isSuspend || declaration.isInline || declaration.isExpect || declaration.isExternal) {         return super.visitFunctionNew(declaration)     }      if (declaration is IrSimpleFunction) {         // 过滤中缀函数,尾递归函数,利用符函数         if (declaration.isInfix || declaration.isTailrec || declaration.isOperator) {             return super.visitFunctionNew(declaration)         }     }      // 过滤函数体为空的函数,不包罗 `Proxy` 注解的函数     if (declaration.body == null || !declaration.hasAnnotation(annotationClass)) {         return super.visitFunctionNew(declaration)     }     ...... }

  • 过滤挂起函数,内联函数,多平台声明函数,外部函数(JNI),
  • 过滤中缀函数,尾递归函数,利用符函数,
  • 过滤函数体为空的函数,不包罗 Proxy 注解的函数。
2.获取

override fun visitFunctionNew(declaration: IrFunction): IrStatement {     ......      val param = SwordParam()     param.hasProxyAnnotation = true      // 获取函数上 `Proxy` 注解的 `IrElement`, 返回 `IrConstructorCall`     val irProxyConstructorCall = declaration.annotations.filter {         it.isAnnotation(proxyAnnotationFqName)     }.toList().single()      // 获取 `Proxy.enable` 属性的值并存储     val enableParam = irProxyConstructorCall.getValueArgument(0)     enableParam?.let {         if (it is IrConst<*>) {             param.enable = it.value as Boolean         }     }      // 获取 `Proxy.handler` 属性的值并存储     val handlerParam = irProxyConstructorCall.getValueArgument(1)     handlerParam?.let {         if (it is IrConst<*>) {             param.handler = it.value as String         }     }      ...... }获取 Proxy 注解中的数据,比力贫苦一些,一开始笔者以为获取到的注解大概雷同于 IrAnnotation,然而发现却是 IrConstructorCall,背面细致想来注解不就是通过构造函数构建一个注解实例么?我们在注解中传入的参数都是赋值给了其构造函数的属性。
通过 getValueArgument() 函数根据注解中声明属性的顺序获取其对应的属性,由于属性大概有默认值,我们在利用时可以不传入某个属性,好比:
@Proxy(     // enable = true,     handler = HandlerFqName.GET_TEXT_NO_ARG ) fun getTextNoArg() = "guodongAndroid"在 Proxy 注解中 enable 默以为 True,以是我们在利用时可以不传入 enable 的值,使其利用默认值,但是对于 kotlin IR 来说,没有明白利用的属性,通过 getValueArgument() 函数获取到的为 null,由于在 Kotlin IR 语法树中找不到这个属性。
由于注解中的属性值必须是编译期常量,以是我们可以把 handlerParam 转换为 IrConst 并获取它的值。
3.校验

override fun visitFunctionNew(declaration: IrFunction): IrStatement {     ......      // 获取 ClassName     val className: String = getClassName(declaration)     // 获取 MethodName     val methodName = declaration.name.asString()      // 校验 开启署理后是否注入了 `handler`     if (param.enable && (param.handler.isEmpty() || param.handler.isBlank())) {         messageCollector.report(             CompilerMessageSeverity.ERROR,             "[$className.$methodName]启用署理后请注入`handler`",         )     }      ...... }  private fun getClassName(     declaration: IrFunction, ): String {     val parentClassOrNull = declaration.parentClassOrNull     val fileOrNull = declaration.fileOrNull      return when {         declaration.isLocal -> {             // 当地方法: 类名.函数名.<anonymous>             // 源码中有此逻辑, 逻辑较为繁琐,且不是 `Sword` 的焦点逻辑,本文就不记录了         }         parentClassOrNull != null -> {             // 获取类名             parentClassOrNull.name.asString()         }         fileOrNull != null -> {             // 假如是顶级函数,获取文件名或`JvmName`注解指定的名字             val annotations = fileOrNull.annotations             if (annotations.hasAnnotation(jvmNameAnnotationFqName)) {                 val annotation = annotations.findAnnotation(jvmNameAnnotationFqName)!!                 val expression = annotation.getValueArgument(0)                 if (expression != null && expression is IrConst<*>) {                     expression.value as String                 } else {                     fileOrNull.name                 }             } else {                 fileOrNull.name             }         }         else -> "Unknown"     } }获取 ClassName 时有以下几点思量:

  • 起首判定是否是当地方法,假如是当地方法则获取 类名.方法名.[<anonymous>],
  • 其次获取当前函数地点的父级 IrClass,假如不为 null,则利用类名,
  • 末了获取函数地点的 IrFile,假如不为 null,再判定文件上是否有 JvmName 注解,有的话利用 JvmName 注解指定的名字,否则利用文件名。
获取 MethodName 时直接利用了函数名称,此处没有判定函数上是否有 JvmName 注解逻辑,信赖读者可以自行扩展。
下面就是一个开启署理后必须注入 handler 的校验逻辑。
4.转换

在前面先容 dump 函数时我们并没有现实上看看 dump 函数的输出内容,接下来让我们看看它输出的语法树信息。
以下面的函数为例:
@Proxy(     enable = true,     handler = HandlerFqName.GetTextArgInvocationHandler ) fun testHandler(): User {     return GetTextArgInvocationHandler().invoke("Test", "testHandler", emptyArray()) as User }dump 函数的输出效果如下:
FUN name:testHandler visibility:public modality:FINAL <> ($this:com.guodong.android.sword.app.Test) returnType:com.guodong.android.sword.app.User   annotations:     Proxy(enable = 'true', handler = 'com.guodong.android.sword.app.GetTextArgInvocationHandler')   $this: VALUE_PARAMETER name:<this> type:com.guodong.android.sword.app.Test   BLOCK_BODY     RETURN type=kotlin.Nothing from='public final fun testHandler (): com.guodong.android.sword.app.User declared in com.guodong.android.sword.app.Test'       TYPE_OP type=com.guodong.android.sword.app.User origin=CAST typeOperand=com.guodong.android.sword.app.User         CALL 'public open fun invoke (className: kotlin.String, methodName: kotlin.String, args: kotlin.Array<kotlin.Any?>): kotlin.Any? declared in com.guodong.android.sword.app.GetTextArgInvocationHandler' type=kotlin.Any? origin=null           $this: CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in com.guodong.android.sword.app.GetTextArgInvocationHandler' type=com.guodong.android.sword.app.GetTextArgInvocationHandler origin=null           className: CONST String type=kotlin.String value="Test"           methodName: CONST String type=kotlin.String value="testHandler"           args: CALL 'public final fun emptyArray <T> (): kotlin.Array<T of kotlin.ArrayIntrinsicsKt.emptyArray> [inline] declared in kotlin.ArrayIntrinsicsKt' type=kotlin.Array<kotlin.Any?> origin=null             <T>: kotlin.Any?fun testHandler(): User
FUN name:testHandler visibility:public modality:FINAL <> ($this:com.guodong.android.sword.app.Test) returnType:com.guodong.android.sword.app.User这是 testHandler 函数的界说。它界说了函数的名称,可见性,模态以及范例署名。我们可以清晰的看到它是一个 public 和 final 名为 testHandler 的函数,并且它有一个隐含的参数 this(()),但是没有范例参数(<>),末了返回值为 com.guodong.android.sword.app.User。
类中的非静态函数(构造函数除外)都有一个隐含的 this 参数:
$this: VALUE_PARAMETER name:<this> type:com.guodong.android.sword.app.TestGetTextArgInvocationHandler()
$this: CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in com.guodong.android.sword.app.GetTextArgInvocationHandler' type=com.guodong.android.sword.app.GetTextArgInvocationHandler origin=null调用 GetTextArgInvocationHandler 的无参构造方法。
Test & testHandler
className: CONST String type=kotlin.String value="Test" methodName: CONST String type=kotlin.String value="testHandler"Test 和 testHandler 作为一个常量字符串。
emptyArray()
args: CALL 'public final fun emptyArray <T> (): kotlin.Array<T of kotlin.ArrayIntrinsicsKt.emptyArray> [inline] declared in kotlin.ArrayIntrinsicsKt' type=kotlin.Array<kotlin.Any?> origin=null             <T>: kotlin.Any?emptyArray() 函数是有范例参数的,因编译器推导我们在编写代码时可以省略,但是在 Kotlin IR 中明白体现了它的范例参数(<T>: kotlin.Any?),以是我们在转换代码时须要留意此处细节,否则转换代码在编译期不会报错,在运行时会抛出非常
invoke("Test", "testHandler", emptyArray())
CALL 'public open fun invoke (className: kotlin.String, methodName: kotlin.String, args: kotlin.Array<kotlin.Any?>): kotlin.Any? declared in com.guodong.android.sword.app.GetTextArgInvocationHandler' type=kotlin.Any? origin=null这是 invoke 函数的调用,清晰体现调用 invoke 函数须要三个参数以及参数的范例,同时函数返回值为 kotlin.Any?
as String
TYPE_OP type=com.guodong.android.sword.app.User origin=CAST typeOperand=com.guodong.android.sword.app.User这是强转利用,TYPE_OP 体现一种范例利用,origin=CAST 体现范例利用符。
return
RETURN type=kotlin.Nothing from='public final fun testHandler (): com.guodong.android.sword.app.User declared in com.guodong.android.sword.app.Test'在 Kotlin 中 return 着实是一个表达式,以是此处 return 的 type 是 kotlin.Nothing 。
通过上面 dump 函数输出语法树的分析,我们已经知道转换后函数体的语法信息,接下来让我们根据上面的分析依次来实现吧。
val handlerConstructorSymbol =     pluginContext.referenceConstructors(FqName(param.handler)).single {         it.owner.valueParameters.isEmpty()     }起首通过 pluginContext.referenceConstructors() 查找 Handler 类的无参构造函数的符号。
val invokeSymbol =     pluginContext.referenceFunctions(FqName("${param.handler}.$INVOKE_METHOD_NAME"))         .single {             val valueParameters = it.owner.valueParameters             valueParameters.size == 3 &&                     valueParameters[0].type == stringType &&                     valueParameters[1].type == stringType &&                     valueParameters[2].type == arrayAnyNType         }接下来通过 pluginContext.referenceFunctions() 查找 Handler 类的 invoke 函数,大概存在函数重载,须要通过 single 函数确定我们须要的函数,这个在前面 IrType 举过例子。
private fun IrBuilderWithScope.irSwordArrayParams(function: IrFunction): IrCall {     val parameters = function.valueParameters     return if (parameters.isEmpty()) {         irCall(emptyArraySymbol).also {             it.putTypeArgument(0, anyNType)         }     } else {         irCall(arrayOfSymbol).also {             val expressions = parameters.map { parameter -> irGet(parameter) }             it.putValueArgument(0, irVararg(anyNType, expressions))         }     } }上面代码是组装 invoke 函数的第三个参数 args: Array<Any?>,当前函数没有参数时利用 emptyArray<Any?>(),有参数时利用 arrayOf(vararg elements: T)。
val invokeCall = irCall(invokeSymbol).apply {     dispatchReceiver = irCallConstructor(handlerConstructorSymbol, emptyList())     putValueArgument(0, irString(className))     putValueArgument(1, irString(methodName))     putValueArgument(2, irSwordArrayParams(function)) }构造 Handler 实例并调用它的 invoke 函数。
val irReturn = irReturn(     typeOperator(         resultType = function.returnType,         argument = invokeCall,         typeOperator = IrTypeOperator.CAST,         typeOperand = function.returnType     ) )typeOperator 是语法树中的 TYPE_OP,就是强转利用,irReturn 体现 Kotlin 中的 return 表达式。
private fun irSword(     function: IrFunction,     param: SwordParam,     className: String,     methodName: String, ): IrBlockBody {     return DeclarationIrBuilder(pluginContext, function.symbol).irBlockBody {         ......         +irReturn     } }通过 + 利用符(unaryPlus)链接整个返回表达式到函数体,末了用新的函数体更换原函数体到达署理的功能:
override fun visitFunctionNew(declaration: IrFunction): IrStatement {     ......      if (param.enable) {         declaration.body = irSword(declaration, param, className, methodName)     }      return super.visitFunctionNew(declaration) }至此,利用 Kotlin IR 为 Kotlin 函数增长署理功能完成。
总结

本文简朴记录了通过 Kotlin IR 实现 Sword 署理功能的过程,同时简朴先容了一些 Kotlin IR 的 API 以及笔者对这些 API 的个人明白。
渴望可以帮您开发本身的 Kotlin 编译器插件,happy~
作者:guodongAndroid
链接:https://juejin.cn/post/7157576701986209828
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2025-2-1 05:59, Processed in 0.177759 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表