$30 off During Our Annual Pro Sale. View Details »

K2のKotlin IDEプラグインの中を覗いてみよう♪

Yan
June 21, 2024

K2のKotlin IDEプラグインの中を覗いてみよう♪

この春、K2コンパイラを使った、IntelliJ IDEAのKotlinプラグインのアルファ版が発表されました。見慣れたIDEが軽くなり、さらに、スイスイとコードを書ける感覚を目指したプラグインの開発がどんどん進んでいます。
K2の洗練された仕組みによって、コードの解析がかなり早くなりました。しかも、プラグインに新しいコンパイラが入っただけではありません。早くて安定するために、それぞれの部分をゼロから作り直したり、大幅に改善されたりしています。

Kotlinチームのメンバーとして、その変化について具体的にお話しさせていただきます。
難しそうだと感じる人もいるかもしれませんが、コンパイラやIDEの内部に触れたことがない人でもわかりやすい内容になっています。皆さんがよく知っているKotlinに、違う角度から触れてみましょう!

Yan

June 21, 2024
Tweet

More Decks by Yan

Other Decks in Programming

Transcript

  1. Jet

  2. Jet

  3. Jetpack Compose Let’s write UI in Kotlin! Code tr a

    nsform a tion needed Implement in the new BE
  4. Jetpack Compose Let’s write UI in Kotlin! Code tr a

    nsform a tion needed Implement in the new BE
  5. Gradle Kotlin DSL Let’s write build scripts in Kotlin! Not

    enough script m a gic Design extensible script de f initions
  6. Gradle Kotlin DSL Let’s write build scripts in Kotlin! Not

    enough script m a gic Design extensible script de f initions
  7. Sometimes, it goes the hard way expect/act u a l

    binding Mixed KMP compil a tion
  8. Sometimes, it goes the hard way expect/act u a l

    binding Mixed KMP compil a tion
  9. K2

  10. Parser fu n main(a r gs: A rr a y

    <St r ing>) { va l name = a r gs.sing l eO r N ull () ?: "Anon y mo u s" p r int l n("He ll o, $name!") }
  11. Parser fu n main(a r gs: A rr a y

    <St r ing>) { va l name = a r gs.sing l eO r N ull () ?: "Anon y mo u s" p r int l n("He ll o, $name!") } main a r gs: A rr a y <St r ing> A rr a y <St r ing> St r ing A rr a y va l name = a r gs.sing l eO r N ull () ?: "Anon y mo u s" p r int l n("He l l o, $name!") name a r gs.sing l eO r N ull () ?: "Anon y mo u s" a r gs.sing l eO r N ull () "Anon y mo u s" a r gs sing l eO r N ull p r int l n "He ll o, $name!" a r gs “He ll o, " name
  12. Parser fu n main(a r gs: A rr a y

    <St r ing>) { va l name = a r gs.sing l eO r N ull () ?: "Anon y mo u s" p r int l n("He ll o, $name!") } main a r gs: A rr a y <St r ing> A rr a y <St r ing> St r ing A rr a y va l name = a r gs.sing l eO r N ull () ?: "Anon y mo u s" p r int l n("He l l o, $name!") name a r gs.sing l eO r N ull () ?: "Anon y mo u s" a r gs.sing l eO r N ull () "Anon y mo u s" a r gs sing l eO r N ull p r int l n "He ll o, $name!" a r gs “He ll o, " name fun println(text: String) { throw RuntimeException(text) // 😈 }
  13. Semantic Analyzer Type resolution C a ll resolution Type inference

    V a rious checkers P a rser Code gener a tion Addition a l st a tic checks ☕.c l ass
  14. Compiler IDE Plugin Use the s a me API! BindingContext

    Dec l a r ationDesc r ipto r K ot l inT y pe Reso l vedCa ll ana l y ze()
  15. Compiler IDE Plugin BindingContext Dec l a r ationDesc r

    ipto r K ot l inT y pe Reso l vedCa ll ana l y ze() Debugger Ref a ctorings Completion Inspections Type Hier a rchy Highlighting
  16. Compiler IDE Plugin BindingContext Dec l a r ationDesc r

    ipto r K ot l inT y pe Reso l vedCa ll ana l y ze() Debugger Ref a ctorings Completion Inspections Type Hier a rchy Highlighting K2
  17. How compilers work Lexer P a rser Sem a ntic

    a n a lyzer Code Gener a tor
  18. Multi-module Compilation B a se App Kotlin Stdlib Coroutines Compose

    module module libr a ry libr a ry libr a ry Compil a tion of B a se Kotlin Stdlib Coroutines B a se Compil a tion of App Kotlin Stdlib Coroutines B a se.j a r Compose App
  19. Inside the IDE B a se App Kotlin Stdlib Coroutines

    Compose module module libr a ry libr a ry libr a ry
  20. Lazy Compiler class LazyClassDescriptor(val psi: KtClassOrObject, storageManager: StorageManager) { val

    companionObject: ClassDescriptor by storageManager.createLazyValue(::computeCompanionObject) private fun computeCompanionObject(): ClassDescriptor { ... } } Compiler represent a tion of a cl a ss (simpli f ied) interface StorageManager { fun <K, V : Any> createMemoizedFunction(compute: (K) -> V): MemoizedFunctionToNotNull<K, V> fun <T : Any> createLazyValue(computable: () -> T): NotNullLazyValue<T> fun <T : Any> createNullableLazyValue(computable: () -> T?): NullableLazyValue<T> fun <T> compute(computable: () -> T): T } L a zy v a lue provider (simpli f ied)
  21. One lock to rule them all class LockBasedStorageManager(val lock: SimpleLock)

    : StorageManager { override fun <T : Any> createLazyValue(computable: () -> T): NotNullLazyValue<T> { return LockBasedNotNullLazyValue<T>(computable) } private inner class LockBasedNotNullLazyValue<T : Any>(val computable: () -> T) : NullableLazyValue<T> { override fun isComputed(): Boolean { ... } override fun isComputing(): Boolean { ... } override fun invoke(): T? { lock.lock() try { // Computation and caching } finally { lock.unlock() } } } } The only l a zy v a lue provider implement a tion (simpli f ied)
  22. Here we go again enum class FirResolvePhase { RAW_FIR, IMPORTS,

    COMPILER_REQUIRED_ANNOTATIONS, COMPANION_GENERATION, SUPER_TYPES, SEALED_CLASS_INHERITORS, TYPES, STATUS, EXPECT_ACTUAL_MATCHING, CONTRACTS, IMPLICIT_TYPES_BODY_RESOLVE, CONSTANT_EVALUATION, ANNOTATION_ARGUMENTS, BODY_RESOLVE } No implicit l a ziness!
  23. SUPER_TYPES Phase sealed class ConeKotlinType : ConeKotlinTypeProjection(), KotlinTypeMarker, TypeArgumentListMarker {

    final override val kind: ProjectionKind get() = ProjectionKind.INVARIANT abstract val typeArguments: Array<out ConeTypeProjection> final override val type: ConeKotlinType get() = this abstract val nullability: ConeNullability abstract val attributes: ConeAttributes final override fun toString(): String { return renderForDebugging() } abstract override fun equals(other: Any?): Boolean abstract override fun hashCode(): Int }
  24. TYPES Phase sealed class ConeKotlinType : ConeKotlinTypeProjection(), KotlinTypeMarker, TypeArgumentListMarker {

    final override val kind: ProjectionKind get() = ProjectionKind.INVARIANT abstract val typeArguments: Array<out ConeTypeProjection> final override val type: ConeKotlinType get() = this abstract val nullability: ConeNullability abstract val attributes: ConeAttributes final override fun toString(): String { return renderForDebugging() } abstract override fun equals(other: Any?): Boolean abstract override fun hashCode(): Int }
  25. BODY_RESOLVE Phase sealed class ConeKotlinType : ConeKotlinTypeProjection(), KotlinTypeMarker, TypeArgumentListMarker {

    final override val kind: ProjectionKind get() = ProjectionKind.INVARIANT abstract val typeArguments: Array<out ConeTypeProjection> final override val type: ConeKotlinType get() = this abstract val nullability: ConeNullability abstract val attributes: ConeAttributes final override fun toString(): String { return renderForDebugging() } abstract override fun equals(other: Any?): Boolean abstract override fun hashCode(): Int }
  26. fun isSubClassOf(subClass: FirClass, superClass: FirClass, useSiteSession: FirSession): Boolean { subClass.lazyResolveToPhase(FirResolvePhase.SUPER_TYPES)

    if (subClass.superConeTypes.any { it.toRegularClassSymbol(useSiteSession) == superClass.symbol }) { return true } return subClass.superConeTypes .asSequence() .mapNotNull { it.toRegularClassSymbol(useSiteSession) } .any { isSubClassOf(it.fir, superClass, useSiteSession) } } No implicit laziness Now super types a re resolved for s u bC l ass
  27. Why a re you telling me a ll of this?

    🤔 🤔 🤔 🤔 🤔 🤔 🤔
  28. Debugger Ref a ctorings Completion Intentions Type Hier a rchy

    Highlighting C a ll Hier a rchy Post f ix templ a tes N a vig a tion J a v a to Kotlin Converter Find Us a ges Quick Fixes Line M a rkers
  29. Debugger Ref a ctorings Completion Intentions Type Hier a rchy

    Highlighting C a ll Hier a rchy Post f ix templ a tes N a vig a tion J a v a to Kotlin Converter Find Us a ges Quick Fixes Line M a rkers
  30. Local phases enum class FirResolvePhase { RAW_FIR, IMPORTS, COMPILER_REQUIRED_ANNOTATIONS, COMPANION_GENERATION,

    SUPER_TYPES, SEALED_CLASS_INHERITORS, TYPES, STATUS, EXPECT_ACTUAL_MATCHING, CONTRACTS, IMPLICIT_TYPES_BODY_RESOLVE, CONSTANT_EVALUATION, ANNOTATION_ARGUMENTS, BODY_RESOLVE }
  31. Local phases enum class FirResolvePhase { RAW_FIR, IMPORTS, COMPILER_REQUIRED_ANNOTATIONS, COMPANION_GENERATION,

    SUPER_TYPES, SEALED_CLASS_INHERITORS, TYPES, STATUS, EXPECT_ACTUAL_MATCHING, CONTRACTS, IMPLICIT_TYPES_BODY_RESOLVE, CONSTANT_EVALUATION, ANNOTATION_ARGUMENTS, BODY_RESOLVE }
  32. Jumping phases enum class FirResolvePhase { RAW_FIR, IMPORTS, COMPILER_REQUIRED_ANNOTATIONS, COMPANION_GENERATION,

    SUPER_TYPES, SEALED_CLASS_INHERITORS, TYPES, STATUS, EXPECT_ACTUAL_MATCHING, CONTRACTS, IMPLICIT_TYPES_BODY_RESOLVE, CONSTANT_EVALUATION, ANNOTATION_ARGUMENTS, BODY_RESOLVE }
  33. Jumping phases enum class FirResolvePhase { RAW_FIR, IMPORTS, COMPILER_REQUIRED_ANNOTATIONS, COMPANION_GENERATION,

    SUPER_TYPES, SEALED_CLASS_INHERITORS, TYPES, STATUS, EXPECT_ACTUAL_MATCHING, CONTRACTS, IMPLICIT_TYPES_BODY_RESOLVE, CONSTANT_EVALUATION, ANNOTATION_ARGUMENTS, BODY_RESOLVE }
  34. Jumping phases enum class FirResolvePhase { RAW_FIR, IMPORTS, COMPILER_REQUIRED_ANNOTATIONS, COMPANION_GENERATION,

    SUPER_TYPES, SEALED_CLASS_INHERITORS, TYPES, STATUS, EXPECT_ACTUAL_MATCHING, CONTRACTS, IMPLICIT_TYPES_BODY_RESOLVE, CONSTANT_EVALUATION, ANNOTATION_ARGUMENTS, BODY_RESOLVE } 100% p a r a llel!
  35. // c l ass ve r sion 65.0 (65) //

    access fl ags 0x21 p u b l ic c l ass test/MainJava { / / compi l ed fr om: MainJava.java / / access fl ags 0x1 p u b l ic <init>()V / / access fl ags 0x9 p u b l ic static main([Ljava/ l ang/St r ing;)V } Java metadata
  36. // c l ass ve r sion 52.0 (52) //

    access fl ags 0x31 p u b l ic f ina l c l ass test/Main K ot l in K t { / / compi l ed fr om: Main K ot l in. k t @L k ot l in/Metadata;( mv={1, 9, 0}, k =2, xi=48, d1={“\ u 0000\ u 0014\n\ u 0000 .. . \ u 0006\ u 0006”}, d2={"main", "", "a r gs", "", "", "([Ljava/ l ang/St r ing;)V", “Test"} ) / / access fl ags 0x19 p u b l ic f ina l static main([Ljava/ l ang/St r ing;)V // annotab l e pa r amete r co u nt: 1 (invisib l e) @Lo r g/jetb r ains/annotations/NotN u ll ;() / / invisib l e, pa r amete r 0 } Kotlin Metadata
  37. message T y peA l ias { optiona l int32

    f l ags = 1 [de f a ul t = 6 /* p u b l ic, no annotations */ ]; r eq u i r ed int32 name = 2 [(name_id_in_tab l e) = t ru e]; r epeated T y pePa r amete r t y pe_pa r amete r = 3; optiona l T y pe u nde rly ing_t y pe = 4; optiona l int32 u nde rl y ing_t y pe_id = 5 [(t y pe_id_in_tab l e) = t ru e]; optiona l T y pe expanded_t y pe = 6; optiona l int32 expanded_t y pe_id = 7 [(t y pe_id_in_tab l e) = t ru e]; r epeated Annotation annotation = 8; / / Index into the Ve r sionReq u i r ementTab l e r epeated int32 ve r sion_ r eq u i r ement = 31; Extensions 100 to 199; } Protobuf inside
  38. Lazy libraries class DeserializedClassDescriptor( outerContext: DeserializationContext, val classProto: ProtoBuf.Class, nameResolver:

    NameResolver ) : AbstractClassDescriptor(...), DeserializedDescriptor { private val primaryConstructor = c.storageManager.createNullableLazyValue { computePrimaryConstructor() } private fun computePrimaryConstructor(): ClassConstructorDescriptor? { if (kind.isSingleton) { return DescriptorFactory.createPrimaryConstructorForObject(this, SourceElement.NO_SOURCE).apply { returnType = getDefaultType() } } return classProto.constructorList.firstOrNull { !Flags.IS_SECONDARY.get(it.flags) }?.let { constructorProto -> c.memberDeserializer.loadConstructor(constructorProto, true) } } ... } Compiler represent a tion of a libr a ry cl a ss (simpli f ied)
  39. pac k age test fu n main(a r gs: A

    rr a y <St r ing>) { va l name = a r gs.sing l eO r N ull () ?: "Anon y mo u s" p r int l n("He ll o, $name!") } PAC K AGE_DIRECTIVE REFERENCE_EXPRESSION(name = test) IMPORT_LIST FUN( f qName = test.main, isExtension = f a l se, hasB l oc k Bod y = f a l se) VALUE_PARAMETER_LIST VALUE_PARAMETER(name = a r gs, hasDe f a u l tVa l u e = f a l se) TYPE_REFERENCE USER_TYPE REFERENCE_EXPRESSION(name = A rr a y ) TYPE_ARGUMENT_LIST TYPE_PROJECTION( k ind = NONE) TYPE_REFERENCE USER_TYPE REFERENCE_EXPRESSION(name = St r ing) Gener a ted stub Source f ile
  40. Kotlin Analysis API Provides bin a ry comp a tibility

    Documented Migr a tion guides Coming this summer