概览 Compose compiler是compose框架的一部分, 本质是一个kcp(kotlin compiler plugin),用于支持编译期针对compose特性进行静态检查和代码生成.
在进入compose的编译之前, 需要事先对kotlin的编译架构、过程以及概念做一些探究储备
kotlin编译过程简介 The Road to the K2 Compiler | The Kotlin Blog
Frontend(编译器前端):对源代码分析得到 AST (抽象语法树)以及符号表,并完成静态检查
Backend(编译器后端):基于 AST/IR等前端产物,生成平台目标代码 kotlin的编译有 k1和k2两个版本, 目前最新版都已切换成k2架构.
下面我们了解一下k1和k2
k1编译器
Frontend 1 2 3 4 5 6 7 8 9 10 11 12 13 14 interface Cat { fun void meow () } class Pet :Cat { override fun void meow () {} } void fun main () { if (pet is Cat) { pet.meow() }else { print("*" ) } }
通过parser和语义分析, 生成语法树PSI(Program Structure Interface) 语义信息(BindingContext)
PSI存储对应的语法结构信息 (一颗tree,包含字符串以及调用结构顺序)
BindingContext存储对应的语义信息 ( 这是一个什么类的什么变量、这是一个函数调用)
Bacend
js、jvm后端直接使用生成的语法树和语义信息, 编译对应的后端产物
native后端开始, kotlin团队决定引入中间表示: IR 产物, 通过 IR再生成对应的汇编机器码
k2编译器
K2编译器主要的特性有2点
引入了中间ir表示: 前端FIR + 中间产物IR
引入插件机制: 允许开发者通过注册插件,在前端编译过程 和 后端过程操作IR进行code的生成和变化.
Compose 编译器只能工作在k2编译架构上, 因为compose的compiler实际上是一个KCP(kotlin compile pugin)
Frontend FIR = BindingContxt + PSI 合并.
提升前端静态分析&&检查的性能 (比如频繁修改局部代码, 基于FIR做了大量性能优化) ide的kotlin插件复用了kotlin编译器的前端编译部分
做一些简单的脱糖&&代码生成工作 (K1做不了, 因为k2支持对FIR树结构进行编辑和变化)
Bacend 统一使用中间IR作为输入, 然后进行优化, 最后调用对应平台后端来生成平台目标代码
KCP简介 (kotlin compiler plugin) Kcp 结构开发流程
Gradle Plugin:
Plugin:KCP 是通过 Gradle 配置的,需要定义一个 Gradle 插件,并在 Gradle 中配置 KCP 所需的编译参数。
Subplugin: 建立从 Gradle Plugin 到 Kotlin Plugin 的连接,并将 Gradle 中配置的参数传递给 Kotlin Plugin Kotlin Plugin:
CommandLineProcessor:KCP 的入口,定义 KCP 的 id、解析命令行参数等
ComponentRegister:注册 KCP 中的 Extension 扩展点。它与 CommandLineProcessor 一样都是通过 SPI 调用,需要添加 auto-service 注解
XXExtension:这是实现 KCP 逻辑的地方。Kotlin 提供了许多类型的 Extension 供我们实现。编译器会在前端、后端的各个编译环节中调用 KCP 注册的对应类型的 Extension。例如 ExpressionCodegenExtension 可用来修改 Class 的 Body;ClassBuilderInterceptorExtension 可以修改 Class 的 Definition 等等
举个例子 针对kotlin的数据类 data class People(var name:String,var age:Int,var sex:Int) 对于data类型, kotlin编译器会默认生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public final class People { private String name; private int age; private int sex; final String getName() { return this .name; } public final void setName(String var1) {} public People(String name, int age, int sex) { super (); this .name = name; this .age = age; this .sex = sex; } } }
使用时只能传参: People people=new People(“lei”,23,1); 但是在一些框架中(Kotlin+Spring Boot), 需要反射无参构造函数, 来初始化数据类, 但是data类型的默认生成有参构造, 无法完成这个功能. kotlin官方提供一个NoArg插件 解决这个问题
1 2 3 4 5 6 7 8 9 10 11 12 13 @NoArg data class People (var name:String,var age:Int ,var sex:Int )public class People { public People(){} public People(@NotNull String name, int age, int sex) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 object FirNoArgDeclarationChecker : FirRegularClassChecker() { override fun check (declaration: FirRegularClass , context: CheckerContext , reporter: DiagnosticReporter ) { val source = declaration.source ?: return if (declaration.classKind != ClassKind.CLASS) return val matcher = context.session.noArgPredicateMatcher if (!matcher.isAnnotated(declaration.symbol)) return when { declaration.isInner -> reporter.reportOn(source, KtErrorsNoArg.NOARG_ON_INNER_CLASS_ERROR, context) declaration.isLocal -> reporter.reportOn(source, KtErrorsNoArg.NOARG_ON_LOCAL_CLASS_ERROR, context) } val superClassSymbol = declaration.symbol.getSuperClassSymbolOrAny(context.session) if (superClassSymbol.declarationSymbols.filterIsInstance<FirConstructorSymbol>().none { it.isNoArgConstructor() } && !matcher.isAnnotated(superClassSymbol)) { reporter.reportOn(source, KtErrorsNoArg.NO_NOARG_CONSTRUCTOR_IN_SUPERCLASS, context) } } private fun FirRegularClassSymbol.getSuperClassSymbolOrAny (session: FirSession ) : FirRegularClassSymbol { for (superType in resolvedSuperTypes) { val symbol = superType.fullyExpandedType(session).toRegularClassSymbol(session) ?: continue if (symbol.classKind == ClassKind.CLASS) return symbol } return session.builtinTypes.anyType.type.toRegularClassSymbol(session) ?: error("Symbol for Any not found" ) } private fun FirConstructorSymbol.isNoArgConstructor () : Boolean { return valueParameterSymbols.all { it.hasDefaultValue } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class NoArgIrGenerationExtension (...) : IrGenerationExtension { override fun generate (moduleFragment: IrModuleFragment , pluginContext: IrPluginContext ) { moduleFragment.accept(NoArgIrTransformer(pluginContext, annotations, invokeInitializers), null ) } } private class NoArgIrTransformer ( private val context: IrPluginContext, private val annotations: List<String>, private val invokeInitializers: Boolean ,) : AnnotationBasedExtension, IrElementVisitorVoid { private fun getOrGenerateNoArgConstructor (klass: IrClass ) : IrConstructor = noArgConstructors.getOrPut(klass) { context.irFactory.buildConstructor { startOffset = SYNTHETIC_OFFSET endOffset = SYNTHETIC_OFFSET returnType = klass.defaultType }.also { ctor -> ctor.parent = klass ctor.body = context.irFactory.createBlockBody( ctor.startOffset, ctor.endOffset, listOfNotNull( IrDelegatingConstructorCallImpl( ctor.startOffset, ctor.endOffset, context.irBuiltIns.unitType, superConstructor.symbol, 0 , superConstructor.valueParameters.size ), IrInstanceInitializerCallImpl( ctor.startOffset, ctor.endOffset, klass.symbol, context.irBuiltIns.unitType ).takeIf { invokeInitializers } ) ) } } }
Compose compiler Compose Compiler 是一个 KCP, 核心在于其实现逻辑的Extension . 入口在ComposePlugin.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class ComposeComponentRegistrar : ComponentRegistrar { override fun registerProjectComponents (...) { registerCommonExtensions(project) registerIrExtension(project, configuration) } fun registerCommonExtensions (project: Project ) { StorageComponentContainerContributor.registerExtension( project, ComposableCallChecker() ) StorageComponentContainerContributor.registerExtension( project, ComposableDeclarationChecker() ) StorageComponentContainerContributor.registerExtension( project, ComposableTargetChecker() ) ComposeDiagnosticSuppressor.registerExtension( project, ComposeDiagnosticSuppressor() ) } fun registerIrExtension ( project: Project , configuration: CompilerConfiguration ) { IrGenerationExtension.registerExtension( project, ComposeIrGenerationExtension( liveLiteralsEnabled = liveLiteralsEnabled, liveLiteralsV2Enabled = liveLiteralsV2Enabled, generateFunctionKeyMetaClasses = generateFunctionKeyMetaClasses, sourceInformationEnabled = sourceInformationEnabled, intrinsicRememberEnabled = intrinsicRememberEnabled, decoysEnabled = decoysEnabled, metricsDestination = metricsDestination, reportsDestination = reportsDestination, validateIr = validateIr, ) ) } }
附录