适配 K2 Class Generator
前言
随着 Kotlin 1.9.20 的发布,K2 编译器进一步稳定,相应的 K2 编译器的 API 也进行了调整。其中 Mask 使用的生成 Class 的 API 就在调整之中,所以 Mask 不得不进行适配。
New APIs
在之前的 Kotlin-KCP的应用-第二篇 中我们介绍了 Mask 的实现原理。主要使用 K1 的ClassBuilderInterceptorExtension
扩展来拦截 Class 的生成过程,从而达到修改 Class 的目的。
但是 ClassBuilderInterceptorExtension
这个扩展在 K2 已标记为废弃且不能再使用,其建议迁移至 IrGenerationExtension
和 ClassGeneratorExtension
扩展。
由于目前 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
接口时,只需实现 newField
和 newMethod
接口方法,其他方法交给接口委派,代码如下所示:
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
,所以说 ClassGeneratorAdapter
是 ClassGenerator
和 ClassBuilder
之间的适配器,正如前文所说这两个接口中的方法非常相似。
接下来看 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 的成本。
现 Mask 已支持 Kotlin 1.9.20。