commit | 05ec9778c19dde3cb791d578bc610ff727d704f9 | [log] [tgz] |
---|---|---|
author | Ting-Yuan Huang <[email protected]> | Sat Apr 02 16:01:20 2022 -0700 |
committer | laszio <[email protected]> | Sat Apr 02 16:32:48 2022 -0700 |
tree | 5b68ad213cad3f099773b3ce0075d8d0498b7577 | |
parent | bb0e61396c2906289aedff54c943af7f908960c5 [diff] |
KOTLIN_VERSION_UPDATE: 1.6.20
Kotlin Symbol Processing (KSP) is an API that you can use to develop lightweight compiler plugins. KSP provides a simplified compiler plugin API that leverages the power of Kotlin while keeping the learning curve at a minimum. Compared to KAPT, annotation processors that use KSP can run up to 2x faster.
To learn more about how KSP compares to KAPT, check out why KSP. To get started writing a KSP processor, take a look at the KSP quickstart.
The KSP API processes Kotlin programs idiomatically. KSP understands Kotlin-specific features, such as extension functions, declaration-site variance, and local functions. KSP also models types explicitly and provides basic type checking, such as equivalence and assign-compatibility.
The API models Kotlin program structures at the symbol level according to Kotlin grammar. When KSP-based plugins process source programs, constructs like classes, class members, functions, and associated parameters are easily accessible for the processors, while things like if blocks and for loops are not.
Conceptually, KSP is similar to KType in Kotlin reflection. The API allows processors to navigate from class declarations to corresponding types with specific type arguments and vice-versa. Substituting type arguments, specifying variances, applying star projections, and marking nullabilities of types are also possible.
Another way to think of KSP is as a pre-processor framework of Kotlin programs. If we refer to KSP-based plugins as symbol processors, or simply processors, then the data flow in a compilation can be described in the following steps:
Unlike a full-fledged compiler plugin, processors cannot modify the code. A compiler plugin that changes language semantics can sometimes be very confusing. KSP avoids that by treating the source programs as read-only.
Most processors navigate through the various program structures of the input source code. Before diving into usage of the API, let‘s look at how a file might look from KSP’s point of view:
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
This view lists common things that are declared in the file--classes, functions, properties, and so on.
KSP expects an implementation of the SymbolProcessorProvider
interface to instantiate SymbolProcessor
:
fun interface SymbolProcessorProvider { fun create(environment: SymbolProcessorEnvironment): SymbolProcessor }
While SymbolProcessor
is defined as:
interface SymbolProcessor { fun process(resolver: Resolver): List<KSAnnotated> // Let's focus on this fun finish() {} fun onError() {} }
A Resolver
provides SymbolProcessor
with access to compiler details such as symbols. A processor that finds all top-level functions and non-local functions in top-level classes might look something like this:
class HelloFunctionFinderProcessor : SymbolProcessor() { ... val functions = mutableListOf<String>() val visitor = FindFunctionsVisitor() override fun process(resolver: Resolver) { resolver.getAllFiles().map { it.accept(visitor, Unit) } } inner class FindFunctionsVisitor : KSVisitorVoid() { override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) { classDeclaration.getDeclaredFunctions().map { it.accept(this, Unit) } } override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) { functions.add(function) } override fun visitFile(file: KSFile, data: Unit) { file.declarations.map { it.accept(this, Unit) } } } ... class Provider : SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = ... } }
Some handy links:
Please let us know what you think about KSP by filing a Github issue or connecting with our team in the #ksp
channel in the Kotlin Slack workspace!
Here are some planned features that have not yet been completely implemented:
The table below includes a list of popular libraries on Android and their various stages of support for KSP. If your library is missing, please feel free to submit a pull request.
Library | Status | Tracking issue for KSP |
---|---|---|
Room | Experimentally supported | |
Moshi | Officially supported | |
RxHttp | Officially supported | |
Kotshi | Officially supported | |
Lyricist | Officially supported | |
Lich SavedState | Officially supported | |
gRPC Dekorator | Officially supported | |
Auto Factory | Not yet supported | Link |
Dagger | Not yet supported | Link |
Hilt | Not yet supported | Link |
Glide | Not yet supported | Link |
DeeplinkDispatch | Supported via airbnb/DeepLinkDispatch#323 |