Kotlin-KCP的应用-第二篇 
  前言 
接Kotlin-KCP的应用-第一篇 ,本文是第二篇,以下是本文的目标:
记录如何简单搭建 KCP 开发环境 
使用 KCP 解决第一篇中的问题 
 
  何为KCP?为何不使用KSP? 
  KSP 
KSP 即 Kotlin Symbol Processing(Kotlin符号处理器) ,KSP 目前只能生成代码,不能修改字节码,第一篇中的问题需要修改字节码,因此 KSP 不能满足需求
  KCP 
KCP 即 Kotlin Compiler Plugin(Kotlin编译器插件),在 kotlinc 过程中提供 hook 时机,在此期间可以生成代码、修改字节码等
标准的 KCP 架构如下:
  Plugin 
Gradle 插件,与 Kotlin 无关,在 build.gradle 脚本中提供一个入口 
通过 Gradle 扩展设置配置信息 
 
  Subplugin 
介于 Gradle 和 Kotlin 直接的 APIs 接口 
读取 Gradle 扩展配置信息并写入 SubpluginOptions 
定义编译器插件的唯一ID 
定义 Kotlin 插件的 Maven 坐标信息,便于编译器下载它 
 
  CommandLinProcessor 
设置 Kotlin 插件唯一 ID 
读取 kotlinc -Xplugin 参数 
读取 SubpluginOptions 配置信息,并写入 CompilerConfigurationKeys 
 
  ComponentRegistrar 
读取 CompilerConfigurationKeys 
注册 Extension 到各编译流程 
 
  Extension 
生成代码 
修改字节码 
多种类型的扩展,比如
ExpressionCodegenExtension 
ClassBuilderInterceptorExtension 
StorageComponentContainerContributor 
IrGenerationExtension 
 
 
 
  实现KCP 
  目标 
根据 KCP 的架构,下面一一进行实现
上图是本仓库架构,旨在通过 KCP 在 Java 字节码中 @Hide 注解目标上设置 ACC_SYNTHETIC 标识,使其在 Java 中不能正常调用,达到隐藏 API 的效果
  build.gradle - project level 
在项目级别的 build.gradle 脚本中配置插件依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 buildscript {          ext.kotlin_plugin_id = "com.guodong.android.mask.kcp"                ext.plugin_version = 'x.x.x'  } plugins {          id("com.gradle.plugin-publish" ) version "0.16.0"  apply false                id("com.github.gmazzo.buildconfig" ) version "3.0.3"  apply false      id 'org.jetbrains.kotlin.jvm'  version '1.6.10'  apply false  } 
 
  plugin-gradle 
接下来编写 Gradle 插件,此插件对应 KCP 架构中的 Plugin 和 Subplugin
首先配置下 build.gradle.kts 脚本
  build.gradle.kts - module level 
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 plugins {     id("java-gradle-plugin" )     kotlin("jvm" )     id("com.github.gmazzo.buildconfig" ) } dependencies {     implementation(kotlin("gradle-plugin-api" )) } buildConfig {          packageName("com.guodong.android.mask.kcp.gradle" )               buildConfigField("String" , "KOTLIN_PLUGIN_ID" , "\"${rootProject.extra["kotlin_plugin_id" ]} \"" )               buildConfigField("String" , "KOTLIN_PLUGIN_GROUP" , "\"com.guodong.android\"" )               buildConfigField("String" , "KOTLIN_PLUGIN_NAME" , "\"mask-kcp-kotlin-plugin\"" )               buildConfigField("String" , "KOTLIN_PLUGIN_VERSION" , "\"${rootProject.extra["PLUGIN_VERSION" ]} \"" ) } gradlePlugin {     plugins {         create("Mask" ) {             id = rootProject.extra["kotlin_plugin_id" ] as  String              displayName = "Mask Kcp"              description = "Mask Kcp"              implementationClass = "com.guodong.android.mask.kcp.gradle.MaskGradlePlugin"           }     } } tasks.withType<KotlinCompile> {     kotlinOptions.jvmTarget = "1.8"  } 
 
  MaskGradlePlugin 
创建 MaskGradlePlugin 实现 KotlinCompilerPluginSupportPlugin 接口
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 class  MaskGradlePlugin  : KotlinCompilerPluginSupportPlugin  {    override  fun  apply (target: Project )   = with(target) {         logger.error("Welcome to guodongAndroid mask kcp gradle plugin." )                                }          override  fun  isApplicable (kotlinCompilation: KotlinCompilation <*>)  : Boolean  = true           override  fun  getCompilerPluginId ()  : String = BuildConfig.KOTLIN_PLUGIN_ID          override  fun  getPluginArtifact ()  : SubpluginArtifact = SubpluginArtifact(         groupId = BuildConfig.KOTLIN_PLUGIN_GROUP,         artifactId = BuildConfig.KOTLIN_PLUGIN_NAME,         version = BuildConfig.KOTLIN_PLUGIN_VERSION,     )               override  fun  applyToCompilation (kotlinCompilation: KotlinCompilation <*>)  : Provider<List<SubpluginOption>> {         val  project = kotlinCompilation.target.project         return  project.provider { emptyList() }     } } 
 
至此 Gradle 插件编写完成,是不是很简单😄
  plugin-kotlin 
接下来编写 Kotlin 编译器插件,此插件对应 KCP 架构中的 CommandLineProcessor 、 ComponentRegistrar 和 Extension
首先配置下 build.gradle.kts 脚本
  build.gradle.kts - module level 
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 plugins {     kotlin("jvm" )     kotlin("kapt" )     id("com.github.gmazzo.buildconfig" ) } dependencies {          compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable" )          kapt("com.google.auto.service:auto-service:1.0" )     compileOnly("com.google.auto.service:auto-service-annotations:1.0" ) } buildConfig {          packageName("com.guodong.android.mask.kcp.kotlin" )               buildConfigField("String" , "KOTLIN_PLUGIN_ID" , "\"${rootProject.extra["kotlin_plugin_id" ]} \"" ) } tasks.withType<KotlinCompile> {     kotlinOptions.jvmTarget = "1.8"  } 
 
根据 KCP 架构,下面实现 CommandLineProcessor
  MaskCommandLineProcessor 
创建 MaskCommandLineProcessor 实现 CommandLineProcessor 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @AutoService(CommandLineProcessor::class) class  MaskCommandLineProcessor  : CommandLineProcessor  {         override  val  pluginId: String = BuildConfig.KOTLIN_PLUGIN_ID               override  val  pluginOptions: Collection<AbstractCliOption> = emptyList()                    override  fun  processOption (          option: AbstractCliOption ,         value: String ,         configuration: CompilerConfiguration      )   {        super .processOption(option, value, configuration)     } } 
 
CommandLineProcessor 编写完成
接下来实现 ComponentRegistrar
  MaskComponentRegistrar 
创建 MaskComponentRegistrar 实现 ComponentRegistrar 接口
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 @AutoService(ComponentRegistrar::class) class  MaskComponentRegistrar  : ComponentRegistrar  {    override  fun  registerProjectComponents (          project: MockProject ,         configuration: CompilerConfiguration      )   {                 val  messageCollector =             configuration.get (CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)                                    messageCollector.report(             CompilerMessageSeverity.WARNING,             "Welcome to guodongAndroid mask kcp kotlin plugin"          )                  ClassBuilderInterceptorExtension.registerExtension(             project,             MaskClassGenerationInterceptor(messageCollector)         )     } } 
 
最后实现 Extension
  MaskClassGenerationInterceptor 
创建 MaskClassGenerationInterceptor 实现 ClassBuilderInterceptorExtension 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  MaskClassGenerationInterceptor (    private  val  messageCollector: MessageCollector, ) : ClassBuilderInterceptorExtension {          override  fun  interceptClassBuilderFactory (          interceptedFactory: ClassBuilderFactory ,         bindingContext: BindingContext ,         diagnostics: DiagnosticSink               )  : ClassBuilderFactory = object  : ClassBuilderFactory by  interceptedFactory {                          override  fun  newClassBuilder (origin: JvmDeclarationOrigin )  : ClassBuilder {                          return  MaskClassBuilder(messageCollector, interceptedFactory.newClassBuilder(origin))         }     } } 
 
  MaskClassBuilder 
创建 MaskClassBuilder 继承 DelegatingClassBuilder,实现 getDelegate 方法,复写 newField 和 newMethod 方法
  getDelegate 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class  MaskClassBuilder (    private  val  messageCollector: MessageCollector,     private  val  delegate: ClassBuilder ) : DelegatingClassBuilder() {          private  val  annotations: List<FqName> = listOf(         FqName("com.guodong.android.mask.api.kt.Hide" ),         FqName("com.guodong.android.mask.api.Hide" )     )          override  fun  getDelegate ()  : ClassBuilder = delegate } 
 
  newField 
处理 @Hide 注解目标为字段
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 48 class  MaskClassBuilder (    private  val  messageCollector: MessageCollector,     private  val  delegate: ClassBuilder ) : DelegatingClassBuilder() {          private  val  annotations: List<FqName> = listOf(         FqName("com.guodong.android.mask.api.kt.Hide" ),         FqName("com.guodong.android.mask.api.Hide" )     )     override  fun  newField (          origin: JvmDeclarationOrigin ,         access: Int ,         name: String ,         desc: String ,         signature: String ?,         value: Any ?     )  : FieldVisitor {                 val  field = origin.descriptor as ? FieldDescriptor         	?: return  super .newField(origin, access, name, desc, signature, value)                  if  (annotations.none { field.annotations.hasAnnotation(it) }) {             return  super .newField(origin, access, name, desc, signature, value)         }                  messageCollector.report(             CompilerMessageSeverity.WARNING,             "Mask Class = ${delegate.thisName} , fieldName = $name , originalAccess = $access "          )                  val  maskAccess = access + Opcodes.ACC_SYNTHETIC                  messageCollector.report(             CompilerMessageSeverity.WARNING,             "Mask Class = ${delegate.thisName} , fieldName = $name , maskAccess = $maskAccess "          )                  return  super .newField(origin, maskAccess, name, desc, signature, value)     } } 
 
  newMethod 
处理 @Hide 注解目标为方法/函数,处理逻辑与字段类似
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 override  fun  newMethod (     origin: JvmDeclarationOrigin ,     access: Int ,     name: String ,     desc: String ,     signature: String ?,     exceptions: Array <out  String >? )  : MethodVisitor {         val  function = origin.descriptor as ? FunctionDescriptor     	?: return  super .newMethod(origin, access, name, desc, signature, exceptions)          if  (annotations.none { function.annotations.hasAnnotation(it) }) {         return  super .newMethod(origin, access, name, desc, signature, exceptions)     }          messageCollector.report(         CompilerMessageSeverity.WARNING,         "Mask Class = ${delegate.thisName} , methodName = $name , originalAccess = $access "      )          val  maskAccess = access + Opcodes.ACC_SYNTHETIC          messageCollector.report(         CompilerMessageSeverity.WARNING,         "Mask Class = ${delegate.thisName} , methodName = $name , maskAccess = $maskAccess "      )          return  super .newMethod(origin, maskAccess, name, desc, signature, exceptions) } 
 
至此 Kotlin 编译器插件完成:happy:
  应用KCP 
  build.gradle - project level 
1 2 3 4 5 6 buildscript {     ext.plugin_version = 'x.x.x'      dependencies {         classpath "com.guodong.android:mask-kcp-gradle-plugin:${plugin_version}"      } } 
 
  lib-kotlin/build.gradle - module level 
1 2 3 4 5 6 7 8 9 # lib-kotlin plugins {     id 'com.android.library'      id 'kotlin-android'      id 'kotlin-kapt'      id 'maven-publish'           id 'com.guodong.android.mask.kcp'  } 
 
  lib-kotlin 
1 2 3 4 5 6 interface  InterfaceTest  {         @Hide      fun  testInterface ()  } 
 
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 class  KotlinTest (a: Int ) : InterfaceTest {         @Hide      constructor () : this (-2 )     companion  object  {         @JvmStatic          fun  newKotlinTest ()   = KotlinTest()     }     private  val  binding: LayoutKotlinTestBinding? = null           var  a = a         @Hide  get          @Hide  set      fun  getA1 ()  : Int  {         return  a     }     fun  test ()   {         a = 1000      }     override  fun  testInterface ()   {         println("Interface function test" )     } } 
 
  app 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # MainActivity.java private  void  testKotlinLib ()  {         KotlinTest  test  =  KotlinTest.newKotlinTest();          Log.e(TAG, "testKotlinLib: before --> "  + test.getA1());     test.test();     Log.e(TAG, "testKotlinLib: after --> "  + test.getA1());          test.testInterface();          InterfaceTest  interfaceTest  =  test;          interfaceTest.testInterface(); } 
 
happy:happy:
  参考文献