blob: e346bc6d3c553b3bda28b8f45e8a1c668dc83392 [file] [view] [edit]
# Kotlin stubs architecture
This document describes how Kotlin PSI **stubs** are modeled and built, and how the Kotlin support integrates with the IntelliJ Platform
stub infrastructure. It covers source stubs, binary (compiled) stubs, and the decompiled PSI/light-class views built on top of binaries.
> Scope note: the IntelliJ Kotlin *plugin* lives in a separate repository
> (`JetBrains/intellij-community`). This repo provides the stub interfaces, element types,
> stub builders, decompilers, and their tests.
## Overview: IntelliJ PSI stubs
- IntelliJ uses **stub trees** (a compact, serializable, indexable summary of the PSI) to support fast indexing, navigation, and partial PSI
loading without parsing whole files. See [Stub Indexes](https://plugins.jetbrains.com/docs/intellij/stub-indexes.html) for more details.
- A stub tree mirrors the structural PSI for declarations and references but omits statement bodies and trivia. In Kotlin, only declarations
(and selected modifiers/annotations/references)
are stubbed.
- Each PSI element that can be stubbed has an element type. In Kotlin these extend
`IStubElementType` (platform API) via `KtStubElementType`; an element type knows how to create PSI from an AST node or from a stub,
serialize/deserialize the stub, and decide whether a stub should be created.
- Note: the Kotlin code uses the platform's `IStubElementType` API. The newer platform
`StubElementFactory`/`StubSerializer` API is not used here.
- File-level stubs implement `PsiFileStub` and are the roots consumed by indexes.
## Where things live
| Area | Module / package | What it contains |
|----------------------------------|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Stub interfaces & contracts | `compiler/psi/psi-api` → `org.jetbrains.kotlin.psi.stubs` | `StubInterfaces.kt`, `KotlinFileStubKind.kt`, `KotlinStubVersions.kt`; element-type base `elements/KtStubElementType.java`; API registry `KtStubBasedElementTypes.kt`; base PSI `KtElementImplStub.java` |
| Element-type & stub impls | `compiler/psi/psi-impl` → `…psi.stubs.elements` / `…psi.stubs.impl` | `Kt*ElementType` impls, registry `KtStubElementTypes.java`, indexing glue `StubIndexService.kt`; stub data classes in `…psi.stubs.impl` (`KotlinPropertyStubImpl`, …), `StubUtils`, `KotlinStubOrigin`, `createConstantValue` |
| Shared cls stub builders | `analysis/decompiled/decompiler-to-stubs` → `…analysis.decompiler.stub` | Metadata(proto)→stub builders shared by all binary formats: `ClassClsStubBuilder`, `CallableClsStubBuilder`, `TypeClsStubBuilder`, `typeAliasClsStubBuilding`, `ClsContractBuilder`, … |
| JVM `.class` entry point | `analysis/decompiled/decompiler-to-file-stubs` | `KotlinClsStubBuilder` (`ClsStubBuilder` for `.class`), plus the private `JvmClsAnnotationLoader` |
| Decompiled PSI / text & builtins | `analysis/decompiled/decompiler-to-psi` | `KotlinClassFileDecompiler`, `KotlinDecompiledFileViewProvider`, `KtDecompiledFile`, and the built-ins stub builder `KotlinBuiltInMetadataStubBuilder` |
| Native / KLIB | `analysis/decompiled/decompiler-native` → `…analysis.decompiler.konan` | `KotlinKlibMetadataDecompiler` for `.knm` metadata (`KlibMetaFileType`) |
| Light classes over decompiled | `analysis/decompiled/light-classes-for-decompiled` | Java-facing light classes built on decompiled declarations: `DecompiledLightClassesFactory`, `KtLightClassForDecompiledDeclaration`, `KtLightMethodForDecompiledDeclaration`, … |
| Stub tests | `analysis/stubs` | **Generated** tests (`tests-gen`) + engines/fixtures in `testFixtures` |
## Core Kotlin PSI stub interfaces
Defined in `compiler/psi/psi-api/src/org/jetbrains/kotlin/psi/stubs/StubInterfaces.kt`. All of them are implementation details,
but exposed due to the current technical limitations.
- `KotlinStubElement<T>` — base contract for all Kotlin stubs (`copyInto`, `isEquivalentTo`).
## Element types and stub creation
- `KtStubElementType<StubT, PsiT>` (`compiler/psi/psi-api/.../elements/KtStubElementType.java`) extends `IStubElementType`. It bridges AST/PSI/stub.
- Base PSI for stub-backed elements is `KtElementImplStub`.
- Element types are registered in `KtStubElementTypes` (psi-impl, the `IStubElementType`
instances). API consumers reference element-type constants through `KtStubBasedElementTypes`
(psi-api).
- **Serialization** — each element type writes/reads its fields via `StubOutputStream`/
`StubInputStream` (see e.g. `KtFunctionElementType.serialize/deserialize`). Stub data classes live in
`org.jetbrains.kotlin.psi.stubs.impl`.
- `KotlinElementTypeProvider` (`compiler/psi/psi-api/.../KotlinElementTypeProvider.kt`) is a bridge between element types and their
implementations (`KotlinElementTypeProviderImpl`).
## Stub versions
`KotlinStubVersions` (`compiler/psi/psi-api/.../KotlinStubVersions.kt`). Bumping a version forces reindexing of the corresponding artifact
kind on the next IDE start.
- `SOURCE_STUB_VERSION` — bump on parser/PSI/stub-format changes affecting source `.kt`/`.kts`.
- `BINARY_STUB_VERSION` (private) — base for binary formats; derived constants:
- `CLASSFILE_STUB_VERSION` — JVM `.class` stubs.
- `BUILTIN_STUB_VERSION` — built-in metadata stubs.
- `KLIB_STUB_VERSION` — Native/Common KLIB metadata (`.knm`).
- `JS_STUB_VERSION` is `@Deprecated` — the Kotlin/JS metadata decompiler has been removed.
## Kinds of Kotlin stubs
### 1) Source stubs
Built for `.kt`/`.kts` from the parsed PSI via `KtStubElementType` and the registered element types. Carry package, imports, declarations,
modifiers, annotations, signatures, and selected flags — but no statement bodies. Consumed by indexes (Go To, Find Usages) and by analysis
to locate declarations quickly.
### 2) Compiled / binary stubs
Built from binary artifacts without sources, by reading Kotlin metadata. All formats produce stubs implementing the same `StubInterfaces.kt`
contracts via the shared builders in
`decompiler-to-stubs` (`ClassClsStubBuilder`, `CallableClsStubBuilder`, `TypeClsStubBuilder`, …).
- **JVM `.class`** — entry point `KotlinClsStubBuilder.buildFileStub` → `doBuildFileStub`:
1. Resolve the `KotlinJvmBinaryClass` and its `KotlinClassHeader`.
2. If the metadata version is incompatible, return an "invalid" file stub (`createIncompatibleAbiVersionFileStub`).
3. `createStubBuilderComponents` prepares the class/data finders and `JvmClsAnnotationLoader`.
4. Dispatch on `header.kind`: `CLASS` → `createTopLevelClassStub`; `FILE_FACADE` /
`MULTIFILE_CLASS_PART` → `createFileFacadeStub`; `MULTIFILE_CLASS` → `createMultifileClassStub`.
5. The shared builders construct the tree; a `PsiFileStub<KtFile>` is returned to the platform.
- **Annotations & constants:** `JvmClsAnnotationLoader` (a private class in
`KotlinClsStubBuilder.kt`, not a separately named class) walks the class with
`KotlinJvmBinaryClass` member visitors, collecting member annotations, field initializers, and annotation default values; values are
converted via `createConstantValue`.
- **Built-ins** — `KotlinBuiltInMetadataStubBuilder` (`decompiler-to-psi`), `BUILTIN_STUB_VERSION`.
- **Native / KLIB** — `KotlinKlibMetadataDecompiler` (`decompiler-native`,
`…analysis.decompiler.konan`), `KLIB_STUB_VERSION`, `.knm` (`KlibMetaFileType`).
### 3) Decompiled PSI and light classes
For navigation/quick-doc, the platform builds a read-only text + PSI view over binaries:
- `KotlinClassFileDecompiler` / `KotlinDecompiledFileViewProvider` / `KtDecompiledFile`
(`decompiler-to-psi`) produce decompiled text-backed PSI.
- `light-classes-for-decompiled` builds Java light classes (`KtLightClassForDecompiledDeclaration`,
`KtLightMethodForDecompiledDeclaration`, …) for Java interop.
The decompiled view and the binary file stubs share the same metadata deserialization, so indexes and decompiled views agree on structure
and names.
## Indexing and versioning
- Element types contribute to indexes through `indexStub`, which delegates to `StubIndexService`
(`indexFunction`, `indexProperty`, `indexClass`, …). The Kotlin support registers file- and element-level indexes that consume
`KotlinFileStub` and its children.
- Bump the relevant `KotlinStubVersions` constant whenever stub shape, names, or index-significant flags change.
## Key PSI integration points
- Stub-backed PSI reads children from either the stub or the AST. Example:
`KtClassOrObject.getSuperTypeList()` uses `getStubOrPsiChild(SUPER_TYPE_LIST)`.
- `KtClassOrObject.isLocal()` / `getClassId()` are stub-aware: the stub carries the data; PSI falls back to computation (and resets
`isLocal` on `subtreeChanged`).
## Testing
- Stub behavior is covered by **generated** tests in `analysis/stubs` (`tests-gen`), driven by the engines/fixtures in
`analysis/stubs/testFixtures/org/jetbrains/kotlin/analysis/stubs`:
`AbstractStubsTest`, `AbstractSourceStubsTest`, `AbstractCompiledStubsTest`, `StubsTestEngine`,
`SourceStubsTestEngine`, `CompiledStubsTestEngine` (plus `additionalStubInfoExtractor`,
`TestGenerator`).
- Regenerate the generated tests with `./gradlew generateTests` after adding test data.
## Useful entry points
- `compiler/psi/psi-api/.../psi/stubs/StubInterfaces.kt` — all Kotlin stub contracts.
- `compiler/psi/psi-api/.../psi/stubs/KotlinFileStubKind.kt` — file stub classification.
- `compiler/psi/psi-api/.../psi/stubs/KotlinStubVersions.kt` — versioning.
- `compiler/psi/psi-api/.../psi/stubs/elements/KtStubElementType.java` — AST/PSI/stub bridge.
- `compiler/psi/psi-impl/.../psi/stubs/elements/` — element-type impls, `KtStubElementTypes`,
`StubIndexService`; stub data classes in `…/psi/stubs/impl/`.
- `analysis/decompiled/decompiler-to-stubs/...` — shared cls builders (`ClassClsStubBuilder`, `CallableClsStubBuilder`,
`TypeClsStubBuilder`).
- `analysis/decompiled/decompiler-to-file-stubs/.../KotlinClsStubBuilder.kt` — JVM `.class` entry.
- `analysis/decompiled/decompiler-to-psi/...` — decompiled PSI + `KotlinBuiltInMetadataStubBuilder`.
- `analysis/decompiled/decompiler-native/.../konan/KotlinKlibMetadataDecompiler.kt` — KLIB/Native.
## FAQ
- **Why both binary file stubs and decompiled PSI?** Stubs power indexing and are lightweight for headless operations; decompiled PSI/light
classes give a readable, navigable, Java-interoperable view. They share metadata so they stay consistent.
- **Which stub version do I bump?** `SOURCE_STUB_VERSION` for source `.kt` changes;
`CLASSFILE_STUB_VERSION` for JVM `.class` builders; `BUILTIN_STUB_VERSION` for built-ins;
`KLIB_STUB_VERSION` for Native/KLIB. (JS no longer produces stubs.)
- **Where do I start debugging a missing stub element?** Source: the element's `Kt*ElementType`
and its `shouldCreateStub`. Binary (JVM): `KotlinClsStubBuilder.doBuildFileStub`, then into the shared `ClassClsStubBuilder` /
`CallableClsStubBuilder` in `decompiler-to-stubs`. Compare against the generated stub tests in `analysis/stubs`.