跳至主要內容

适配 K2 Class Generator

guodongAndroid大约 4 分钟Kotlinandroidkotlinkcp

前言

随着 Kotlin 1.9.20 的发布,K2 编译器进一步稳定,相应的 K2 编译器的 API 也进行了调整。其中 Mask 使用的生成 Class 的 API 就在调整之中,所以 Mask 不得不进行适配。

New APIs

在之前的 Kotlin-KCP的应用-第二篇 中我们介绍了 Mask 的实现原理。主要使用 K1 的ClassBuilderInterceptorExtension 扩展来拦截 Class 的生成过程,从而达到修改 Class 的目的。

但是 ClassBuilderInterceptorExtension 这个扩展在 K2 已标记为废弃且不能再使用,其建议迁移至 IrGenerationExtensionClassGeneratorExtension 扩展。

由于目前 Mask 的实现原理是修改字节码,所以这里我们选择 ClassGeneratorExtension 扩展。

ClassGeneratorExtension

ClassGeneratorExtension 是一个接口,其只有一个接口函数 generateClass(generator: ClassGenerator, declaration: IrClass?): ClassGenerator,看到其中有 IrClass 参数,可以说明 K2 已摒弃 K1 的语法树,使用新的 IR 语法树。

接下来实现 ClassGeneratorExtension,代码如下所示:

class MaskClassGeneratorExtension(
    private val messageCollector: MessageCollector,
) : ClassGeneratorExtension {

    override fun generateClass(generator: ClassGenerator, declaration: IrClass?) =
        if (declaration == null) generator else MaskClassGenerator(
            messageCollector,
            generator,
            declaration
        )
}

如果 IrClass 为空则使用原 generator,否则使用 MaskClassGenerator

ClassGenerator

ClassGenerator 也是一个接口,它的接口方法与 K1 中的 ClassBuilder 类似。由于 Mask 只关注 Java 中的字段和方法,所以在实现 ClassGenerator 接口时,只需实现 newFieldnewMethod 接口方法,其他方法交给接口委派,代码如下所示:

class MaskClassGenerator(
    private val messageCollector: MessageCollector,
    private val delegate: ClassGenerator,
    private val irClass: IrClass
) : ClassGenerator by delegate {

    private val annotations: List<FqName> = listOf(
        FqName("com.guodong.android.mask.api.kt.Hide"),
        FqName("com.guodong.android.mask.api.Hide")
    )

    override fun newField(
        declaration: IrField?,
        access: Int,
        name: String,
        desc: String,
        signature: String?,
        value: Any?
    ): FieldVisitor {
        val irField = declaration
            ?: return delegate.newField(declaration, access, name, desc, signature, value)

        if (annotations.none { irField.annotations.hasAnnotation(it) }) {
            return delegate.newField(declaration, access, name, desc, signature, value)
        }

        messageCollector.report(
            CompilerMessageSeverity.WARNING,
            "Mask Class = ${irClass.name.asString()}, fieldName = $name, originalAccess = $access"
        )

        val maskAccess = access + Opcodes.ACC_SYNTHETIC

        messageCollector.report(
            CompilerMessageSeverity.WARNING,
            "Mask Class = ${irClass.name.asString()}, fieldName = $name, maskAccess = $maskAccess"
        )

        return delegate.newField(declaration, maskAccess, name, desc, signature, value)
    }

    override fun newMethod(
        declaration: IrFunction?,
        access: Int,
        name: String,
        desc: String,
        signature: String?,
        exceptions: Array<out String>?
    ): MethodVisitor {
        val irFunction = declaration
            ?: return delegate.newMethod(declaration, access, name, desc, signature, exceptions)

        if (annotations.none { irFunction.annotations.hasAnnotation(it) }) {
            return delegate.newMethod(declaration, access, name, desc, signature, exceptions)
        }

        messageCollector.report(
            CompilerMessageSeverity.WARNING,
            "Mask Class = ${irClass.name.asString()}, methodName = $name, originalAccess = $access"
        )

        val maskAccess = access + Opcodes.ACC_SYNTHETIC

        messageCollector.report(
            CompilerMessageSeverity.WARNING,
            "Mask Class = ${irClass.name.asString()}, methodName = $name, maskAccess = $maskAccess"
        )

        return delegate.newMethod(declaration, maskAccess, name, desc, signature, exceptions)
    }
}

实现思路与之前的 MaskClassBuilder 的别无二致:判断字段和方法上是否有 @Hide 注解,如果有的话则修改字段和方法的访问标识,在原访问标识的基础上增加 Opcodes.ACC_SYNTHETIC 标识。

探秘

到这里你一定想知道 ClassGeneratorExtension 是如何实现的。

首先在 IDE 中查找 ClassGeneratorExtension 的实现类时只能找到我们自己写的实现类,现在我们不妨在 IDE 中查找 ClassGenerator 的实现类,此时你会找到 ClassGeneratorAdapter

private class ClassGeneratorAdapter(
    val irClass: IrClass?,
    val builder: ClassBuilder
) : ClassGenerator {}

ClassGeneratorAdapter 的确实现了 ClassGenerator 接口,但是我们要注意到它的构造参数中有 ClassBuilder,所以说 ClassGeneratorAdapterClassGeneratorClassBuilder 之间的适配器,正如前文所说这两个接口中的方法非常相似。

接下来看 ClassGeneratorAdapter 中的几个方法实现:

// 类定义

override fun defineClass(
	version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<out String>
) {
	builder.defineClass(irClass?.psiElement, version, access, name, signature, superName, interfaces)
}

override fun newField(
	declaration: IrField?, access: Int, name: String, desc: String, signature: String?, value: Any?
): FieldVisitor =
	builder.newField(declaration.wrapToOrigin(), access, name, desc, signature, value)

override fun newMethod(
	declaration: IrFunction?, access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?
): MethodVisitor =
	builder.newMethod(declaration.wrapToOrigin(), access, name, desc, signature, exceptions)

// 其他方法实现

从上述几个方法实现可以看出:ClassGenerator 最终由 ClassBuilder 实现。

ClassGeneratorAdapter 类定义的上面我们还可以看到一个 ExtensionAdapter 类:

@Suppress("DEPRECATION_ERROR")
private class ExtensionAdapter(
    private val extension: ClassGeneratorExtension
) : org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension {
}

它实现了 K1 的 ClassBuilderInterceptorExtension 扩展,并在构造方法中将 K2 的 ClassGeneratorExtension 扩展作为参数传入,显然这个类是 K1 和 K2 扩展接口的适配器,正如它的实现所示:

override fun interceptClassBuilderFactory(
    interceptedFactory: ClassBuilderFactory,
    bindingContext: BindingContext,
    diagnostics: DiagnosticSink,
): ClassBuilderFactory = object : DelegatingClassBuilderFactory(interceptedFactory) {
    override fun newClassBuilder(origin: JvmDeclarationOrigin): DelegatingClassBuilder {
        val classBuilder = interceptedFactory.newClassBuilder(origin)
        val irClass = origin.unwrapOrigin<IrClass>()
        return DelegatingClassBuilderAdapter(
            extension.generateClass(
                ClassGeneratorAdapter(irClass, classBuilder),
                irClass
            ),
            classBuilder
        )
    }
}

现在我们是不是可以这样理解:

ClassGeneratorExtension 是对 ClassBuilderInterceptorExtension 的进一步包装,并在某些方法参数中提供了 IR 类型的参数。

总结

ClassGeneratorExtension 相比 ClassBuilderInterceptorExtension,使用起来更加简单,参数类型更加明确,同时提供了 IR 类型表示,如果使用过 IrGenerationExtension 扩展的话,也降低了学习 IR 的成本。

Maskopen in new window 已支持 Kotlin 1.9.20。