Skip to content

KotlinシンボルプロセッシングAPI

Kotlin Symbol Processing (KSP) は、軽量なコンパイラプラグインを開発するために使用できるAPIです。 KSPは、Kotlinの能力を活用しつつ学習曲線を最小限に抑える、簡素化されたコンパイラプラグインAPIを提供します。 kaptと比較して、KSPを使用するアノテーションプロセッサは最大2倍高速に実行できます。

概要

KSP APIは、Kotlinプログラムを慣用的に処理します。KSPは、拡張関数、宣言サイトの分散(declaration-site variance)、ローカル関数など、Kotlin固有の機能を理解します。 また、型を明示的にモデル化し、等価性や代入互換性などの基本的な型チェックも提供します。

このAPIは、Kotlinの文法に従ってKotlinプログラムの構造をシンボルレベルでモデル化します。 KSPベースのプラグインがソースプログラムを処理する際、クラス、クラスメンバー、関数、および関連するパラメータのような構造がプロセッサからアクセス可能になりますが、ifブロックやforループのようなものはアクセスできません。

概念的には、KSPはKotlinリフレクションのKTypeに似ています。 このAPIにより、プロセッサはクラス宣言から特定の型引数を持つ対応する型へ、またその逆へのナビゲーションを可能にします。 また、型引数を置換し、分散を指定し、スタースプロジェクションを適用し、型のnull許容性をマークできます。

KSPをKotlinプログラムのプリプロセッサフレームワークと考えることもできます。KSPベースのプラグインを シンボルプロセッサ、または単に_プロセッサ_と見なすことで、コンパイルにおけるデータフローを次のステップで記述できます。

  1. プロセッサはソースプログラムとリソースを読み込み、分析します。
  2. プロセッサはコードやその他の形式の出力を生成します。
  3. Kotlinコンパイラは、ソースプログラムと生成されたコードを一緒にコンパイルします。

本格的なコンパイラプラグインとは異なり、プロセッサはコードを変更することはできません。 言語の意味を変更するコンパイラプラグインは、時に非常に混乱を招く可能性があります。 KSPは、ソースプログラムを読み取り専用として扱うことで、これを回避します。

KSPの概要は、こちらのビデオでも確認できます。

KSPがソースファイルをどのように見ているか

ほとんどのプロセッサは、入力ソースコードの様々なプログラム構造をナビゲートします。 APIの利用法に入る前に、KSPの視点からファイルがどのように見えるかを見てみましょう。

text
KSFile
  packageName: KSName
  fileName: String
  annotations: List<KSAnnotation>  (File annotations)
  declarations: List<KSDeclaration>
    KSClassDeclaration // class, interface, object
      simpleName: KSName
      qualifiedName: KSName
      containingFile: String
      typeParameters: KSTypeParameter
      parentDeclaration: KSDeclaration
      classKind: ClassKind
      primaryConstructor: KSFunctionDeclaration
      superTypes: List<KSTypeReference>
      // contains inner classes, member functions, properties, etc.
      declarations: List<KSDeclaration>
    KSFunctionDeclaration // top level function
      simpleName: KSName
      qualifiedName: KSName
      containingFile: String
      typeParameters: KSTypeParameter
      parentDeclaration: KSDeclaration
      functionKind: FunctionKind
      extensionReceiver: KSTypeReference?
      returnType: KSTypeReference
      parameters: List<KSValueParameter>
      // contains local classes, local functions, local variables, etc.
      declarations: List<KSDeclaration>
    KSPropertyDeclaration // global variable
      simpleName: KSName
      qualifiedName: KSName
      containingFile: String
      typeParameters: KSTypeParameter
      parentDeclaration: KSDeclaration
      extensionReceiver: KSTypeReference?
      type: KSTypeReference
      getter: KSPropertyGetter
        returnType: KSTypeReference
      setter: KSPropertySetter
        parameter: KSValueParameter

このビューには、ファイル内で宣言される一般的なもの、例えばクラス、関数、プロパティなどがリストされています。

SymbolProcessorProvider: エントリポイント

KSPは、SymbolProcessorをインスタンス化するためにSymbolProcessorProviderインターフェースの実装を期待します。

kotlin
interface SymbolProcessorProvider {
    fun create(environment: SymbolProcessorEnvironment): SymbolProcessor
}

一方、SymbolProcessorは次のように定義されています。

kotlin
interface SymbolProcessor {
    fun process(resolver: Resolver): List<KSAnnotated> // Let's focus on this
    fun finish() {}
    fun onError() {}
}

Resolverは、シンボルなどのコンパイラの詳細へのアクセスをSymbolProcessorに提供します。 すべてのトップレベル関数と、トップレベルクラス内の非ローカル関数を見つけるプロセッサは、次のようになるでしょう。

kotlin
class HelloFunctionFinderProcessor : SymbolProcessor() {
    // ...
    val functions = mutableListOf<KSClassDeclaration>()
    val visitor = FindFunctionsVisitor()

    override fun process(resolver: Resolver) {
        resolver.getAllFiles().forEach { it.accept(visitor, Unit) }
    }

    inner class FindFunctionsVisitor : KSVisitorVoid() {
        override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
            classDeclaration.getDeclaredFunctions().forEach { it.accept(this, Unit) }
        }

        override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
            functions.add(function)
        }

        override fun visitFile(file: KSFile, data: Unit) {
            file.declarations.forEach { it.accept(this, Unit) }
        }
    }
    // ...
    
    class Provider : SymbolProcessorProvider {
        override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = TODO()
    }
}

リソース

サポートされているライブラリ

以下の表は、Androidで人気のあるライブラリと、KSPの様々なサポート段階を示しています。

ライブラリステータス
Room公式サポート
Moshi公式サポート
RxHttp公式サポート
Kotshi公式サポート
Lyricist公式サポート
Lich SavedState公式サポート
gRPC Dekorator公式サポート
EasyAdapter公式サポート
Koin Annotations公式サポート
Glide公式サポート
Micronaut公式サポート
Epoxy公式サポート
Paris公式サポート
Auto Dagger公式サポート
SealedX公式サポート
Ktorfit公式サポート
Mockative公式サポート
DeeplinkDispatchairbnb/DeepLinkDispatch#323を介してサポート
Daggerアルファ
Motifアルファ
Hilt進行中
Auto Factory未サポート