Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin-friendly Annotation Processing (AppsConf 2018)

Kotlin-friendly Annotation Processing (AppsConf 2018)

When promoting Kotlin over Java to your friend the first argument you’ll probably use will be its powerful syntax: in-house nullability support, default arguments, delegated properties, data & sealed classes, etc. These features are really the stuff we’re benefiting from everyday, but if we’ll take Java annotation processors then sadly the only information they can get about our code will be Java-related and no info about Kotlin-specific features.

In this talk we’ll find out how to use Kotlin metadata added by the compiler to get info about Kotlin language features in the code and utilise it during annotation processing.

Video: https://youtu.be/rJ9BRDuvJ1I

C50a1f407bc251b7395c0984be4327e9?s=128

Sergey Ryabov

October 08, 2018
Tweet

More Decks by Sergey Ryabov

Other Decks in Programming

Transcript

  1. Kotlin-friendly Annotation Processing Sergey Ryabov

  2. JSR-269

  3. • Annotation processing works inside javac JSR-269

  4. • Annotation processing works inside javac • Separate Annotation Processors

    are pluggable JSR-269
  5. • Annotation processing works inside javac • Separate Annotation Processors

    are pluggable • Gradle’s annotationProcessor dependencies configuration JSR-269
  6. • Annotation processing works inside javac • Separate Annotation Processors

    are pluggable • Gradle’s annotationProcessor dependencies configuration • /META-INF/services/javax.annotation.processing.Processor 
 declares your AP’s main entry point JSR-269
  7. • Annotation processing works inside javac • Separate Annotation Processors

    are pluggable • Gradle’s annotationProcessor dependencies configuration • /META-INF/services/javax.annotation.processing.Processor 
 declares your AP’s main entry point • Multiple rounds of processing JSR-269
  8. *.java JSR-269

  9. *.java apt JSR-269

  10. *.java apt javac JSR-269

  11. Kotlin-way

  12. • APT can’t process Kotlin sources — build KAPT Kotlin-way

  13. • APT can’t process Kotlin sources — build KAPT •

    Kotlin’s own JSR-269 implementation? Kotlin-way
  14. • APT can’t process Kotlin sources — build KAPT •

    Kotlin’s own JSR-269 implementation? • Generate Java code/stubs from Kotlin? Kotlin-way
  15. • APT can’t process Kotlin sources — build KAPT •

    Kotlin’s own JSR-269 implementation? • Generate Java code/stubs from Kotlin? • Precompile Kotlin classes and feed them to javac? Kotlin-way
  16. • APT can’t process Kotlin sources — build KAPT •

    Kotlin’s own JSR-269 implementation? • Generate Java code/stubs from Kotlin? • Precompile Kotlin classes and feed them to javac? Kotlin-way
  17. • APT can’t process Kotlin sources — build KAPT •

    Kotlin’s own JSR-269 implementation? • Generate Java code/stubs from Kotlin? • Precompile Kotlin classes and feed them to javac? Kotlin-way
  18. • APT can’t process Kotlin sources — build KAPT •

    Kotlin’s own JSR-269 implementation? • Generate Java code/stubs from Kotlin? • Precompile Kotlin classes and feed them to javac? Kotlin-way
  19. • APT can’t process Kotlin sources — build KAPT •

    Kotlin’s own JSR-269 implementation? • Generate Java code/stubs from Kotlin? • Precompile Kotlin classes and feed them to javac? • No multiple APT rounds over generated Kotlin files Kotlin-way
  20. *.kt Kotlin-way

  21. *.kt kapt Kotlin-way

  22. *.kt kapt *.java Kotlin-way

  23. *.kt kapt apt *.java Kotlin-way

  24. *.kt kapt kotlinc apt *.java Kotlin-way

  25. class KotlinProcessor : AbstractProcessor() { } JSR-269

  26. class KotlinProcessor : AbstractProcessor() { override fun getSupportedOptions(): Set<String> }

    JSR-269
  27. class KotlinProcessor : AbstractProcessor() { override fun getSupportedOptions(): Set<String> override

    fun getSupportedAnnotationTypes(): Set<String> } JSR-269
  28. class KotlinProcessor : AbstractProcessor() { override fun getSupportedOptions(): Set<String> override

    fun getSupportedAnnotationTypes(): Set<String> override fun getSupportedSourceVersion(): SourceVersion } JSR-269
  29. class KotlinProcessor : AbstractProcessor() { override fun getSupportedOptions(): Set<String> override

    fun getSupportedAnnotationTypes(): Set<String> override fun getSupportedSourceVersion(): SourceVersion override fun init(processingEnv: ProcessingEnvironment) } JSR-269
  30. class KotlinProcessor : AbstractProcessor() { override fun getSupportedOptions(): Set<String> override

    fun getSupportedAnnotationTypes(): Set<String> override fun getSupportedSourceVersion(): SourceVersion override fun init(processingEnv: ProcessingEnvironment) override fun process( annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean } JSR-269
  31. class KotlinProcessor : AbstractProcessor() { override fun getSupportedOptions(): Set<String> override

    fun getSupportedAnnotationTypes(): Set<String> override fun getSupportedSourceVersion(): SourceVersion override fun init(processingEnv: ProcessingEnvironment) override fun process( annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean } JSR-269
  32. Problems

  33. Problems • No info about Kotlin-specifics is available for Annotation

    Processors
  34. Problems • No info about Kotlin-specifics is available for Annotation

    Processors • No Nullability
  35. Problems • No info about Kotlin-specifics is available for Annotation

    Processors • No Nullability • No default arguments
  36. Problems • No info about Kotlin-specifics is available for Annotation

    Processors • No Nullability • No default arguments • No sealed & data classes
  37. Problems • No info about Kotlin-specifics is available for Annotation

    Processors • No Nullability • No default arguments • No sealed & data classes • …
  38. Problems • No info about Kotlin-specifics is available for Annotation

    Processors • No Nullability • No default arguments • No sealed & data classes • … • No nothing!
  39. None
  40. Dad! APT can’t get any Kotlin info. And StackOverflow can’t

    help!
  41. Dad! APT can’t get any Kotlin info. And StackOverflow can’t

    help! If even StackOverflow keeps silence…
  42. Dad! APT can’t get any Kotlin info. And StackOverflow can’t

    help! If even StackOverflow keeps silence… B-b-b-but compiler..?
  43. Dad! APT can’t get any Kotlin info. And StackOverflow can’t

    help! If even StackOverflow keeps silence… B-b-b-but compiler..? Well, this city has a Hero!
  44. @Metadata

  45. @Metadata( mv = {1, 1, 11}, bv = {1, 0,

    2}, k = 1, d1 = {"\u0000\u0080\u0001\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u00… d2 = {""Lxyz/ryabov/gsonkot/User;", "", "id", "", "username", "", "isAdmin ) public final class User { ... }
  46. @Metadata

  47. @Metadata • Provides Kotlin-specific info

  48. @Metadata • Provides Kotlin-specific info • Class-level annotation

  49. @Metadata • Provides Kotlin-specific info • Class-level annotation • Accessible

    both at compile time and runtime
  50. @Metadata • Provides Kotlin-specific info • Class-level annotation • Accessible

    both at compile time and runtime • Used by compiler as well
  51. @Metadata • mv • bv • k • d1 •

    d2 • xs • xi • pn
  52. @Metadata • metadataVersion • bytecodeVersion • kind • data1 •

    data2 • extraString • extraInt • packageName
  53. What can we do with that?

  54. What can we do with that? Parse data1 Protocol Buffers

    using kind to define its format
  55. What can we do with that? Parse data1 Protocol Buffers

    using kind to define its format https://github.com/JetBrains/kotlin/blob/master/core/metadata/src/metadata.proto
  56. Protocol Buffers

  57. Protocol Buffers

  58. What tools do we have?

  59. What tools do we have? • Hands

  60. What tools do we have? • Hands

  61. What tools do we have? • Hands • Use kotlin-metadata

    by Eugenio Marletti
 https://github.com/Takhion/kotlin-metadata
  62. What tools do we have? • Hands • Use kotlin-metadata

    by Eugenio Marletti
 https://github.com/Takhion/kotlin-metadata • Or…

  63. What tools do we have? • Hands • Use kotlin-metadata

    by Eugenio Marletti
 https://github.com/Takhion/kotlin-metadata • Or… kotlinx-metadata by JetBrains
 https://github.com/JetBrains/kotlin/tree/master/libraries/kotlinx-metadata/jvm
  64. Two patterns

  65. Two patterns • Visitors API

  66. Two patterns • Visitors API • DOM-like API

  67. Visitors API

  68. Visitors API public interface Element { <R, P> R accept(ElementVisitor<R,

    P> v, P p); }
  69. Visitors API public interface ElementVisitor<R, P> { R visit(Element e,

    P p); }
  70. Visitors API public interface ElementVisitor<R, P> { R visit(Element e,

    P p); R visitPackage(PackageElement e, P p); }
  71. Visitors API public interface ElementVisitor<R, P> { R visit(Element e,

    P p); R visitPackage(PackageElement e, P p); R visitType(TypeElement e, P p); }
  72. Visitors API public interface ElementVisitor<R, P> { R visit(Element e,

    P p); R visitPackage(PackageElement e, P p); R visitType(TypeElement e, P p); R visitVariable(VariableElement e, P p); }
  73. Visitors API public interface ElementVisitor<R, P> { R visit(Element e,

    P p); R visitPackage(PackageElement e, P p); R visitType(TypeElement e, P p); R visitVariable(VariableElement e, P p); R visitExecutable(ExecutableElement e, P p); }
  74. Visitors API public interface ElementVisitor<R, P> { R visit(Element e,

    P p); R visitPackage(PackageElement e, P p); R visitType(TypeElement e, P p); R visitVariable(VariableElement e, P p); R visitExecutable(ExecutableElement e, P p); R visitTypeParameter(TypeParameterElement e, P p); }
  75. Visitors API public interface ElementVisitor<R, P> { R visit(Element e,

    P p); R visitPackage(PackageElement e, P p); R visitType(TypeElement e, P p); R visitVariable(VariableElement e, P p); R visitExecutable(ExecutableElement e, P p); R visitTypeParameter(TypeParameterElement e, P p); R visitUnknown(Element e, P p); }
  76. Visitors API

  77. Visitors API class KotlinProcessor : AbstractProcessor() { override fun process(

    annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean { return false } }
  78. Visitors API class KotlinProcessor : AbstractProcessor() { override fun process(

    annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean { for (element in roundEnv.getElementsAnnotatedWith(Builder::class.java)) { }. return false } }
  79. Visitors API class KotlinProcessor : AbstractProcessor() { override fun process(

    annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean { for (element in roundEnv.getElementsAnnotatedWith(Builder::class.java)) { element.accept(visitor, null) }.. return false } }
  80. Visitors API class KotlinProcessor : AbstractProcessor() { override fun process(

    annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean { for (element in roundEnv.getElementsAnnotatedWith(Builder::class.java)) { val visitor = object : ElementScanner7<Unit, Unit>() { override fun visitVariable(e: VariableElement, p: Unit?) { handleVariable(e) } } element.accept(visitor, null) }. return false } }
  81. Visitors API class KotlinProcessor : AbstractProcessor() { override fun process(

    annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean { for (element in roundEnv.getElementsAnnotatedWith(Builder::class.java)) { val visitor = object : ElementScanner7<Unit, Unit>() { override fun visitVariable(e: VariableElement, p: Unit?) { handleVariable(e) } } element.accept(visitor, null) }. return false } }
  82. Two patterns • Visitors API • DOM-like API

  83. DOM-like API

  84. DOM-like API public interface Element { }

  85. DOM-like API public interface Element { Element getEnclosingElement(); }

  86. DOM-like API public interface Element { Element getEnclosingElement(); List<? extends

    Element> getEnclosedElements(); }
  87. DOM-like API public interface Element { Element getEnclosingElement(); List<? extends

    Element> getEnclosedElements(); ElementKind getKind(); }
  88. DOM-like API class KotlinProcessor : AbstractProcessor() { override fun process(

    annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean { for (element in roundEnv.getElementsAnnotatedWith(BindView::class.java)) { } return false } }
  89. DOM-like API class KotlinProcessor : AbstractProcessor() { override fun process(

    annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean { for (element in roundEnv.getElementsAnnotatedWith(BindView::class.java)) { } return false } }
  90. DOM-like API class KotlinProcessor : AbstractProcessor() { override fun process(

    annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean { for (element in roundEnv.getElementsAnnotatedWith(BindView::class.java)) { if (element.kind == ElementKind.FIELD) { handleVariable(element as VariableElement) }. } return false } }
  91. DOM-like API class KotlinProcessor : AbstractProcessor() { override fun process(

    annotations: Set<TypeElement>, roundEnv: RoundEnvironment ): Boolean { for (element in roundEnv.getElementsAnnotatedWith(BindView::class.java)) { if (element.kind == ElementKind.FIELD) { handleVariable(element as VariableElement) }. } return false } }
  92. kotlinx-metadata

  93. kotlinx-metadata Has only low-level Visitors API

  94. Visitors API

  95. Visitors API abstract class KmFunctionVisitor abstract class KmPropertyVisitor abstract class

    KmPackageVisitor abstract class KmClassVisitor abstract class KmConstructorVisitor abstract class KmValueParameterVisitor abstract class KmLambdaVisitor abstract class KmTypeVisitor abstract class KmTypeAliasVisitor abstract class KmTypeParameterVisitor
  96. Visitors API abstract class KmClassVisitor { open fun visit*(...) }.

  97. Visitors API abstract class KmClassVisitor { open fun visitTypeParameter( flags:

    Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }.
  98. Visitors API class Type<out E : UpperBoundType> abstract class KmClassVisitor

    { open fun visitTypeParameter( flags: Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }.
  99. Visitors API class Type<out E : UpperBoundType> abstract class KmClassVisitor

    { open fun visitTypeParameter( flags: Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }.
  100. Visitors API class Type<out E : UpperBoundType> abstract class KmClassVisitor

    { open fun visitTypeParameter( flags: Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }.
  101. Visitors API class Type<out E : UpperBoundType> abstract class KmClassVisitor

    { open fun visitTypeParameter( flags: Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }.
  102. Visitors API class Type<out E : UpperBoundType> abstract class KmClassVisitor

    { open fun visitTypeParameter( flags: Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }.
  103. Visitors API class Type<out E : UpperBoundType> abstract class KmClassVisitor

    { open fun visitTypeParameter( flags: Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }.
  104. Visitors API class Type<out E : UpperBoundType> abstract class KmClassVisitor

    { open fun visitTypeParameter( flags: Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }.
  105. Visitors API abstract class KmClassVisitor { open fun visitTypeParameter( flags:

    Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }.
  106. Visitors API abstract class KmClassVisitor { open fun visitTypeParameter( flags:

    Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }. interface ElementVisitor<R, P> { R visitTypeParameter( TypeParameterElement e, P p ); }
  107. Visitors API abstract class KmClassVisitor { open fun visitTypeParameter( flags:

    Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }. interface ElementVisitor<R, P> { R visitTypeParameter( TypeParameterElement e, P p ); }
  108. Visitors API abstract class KmClassVisitor { open fun visitTypeParameter( flags:

    Flags, name: String, id: Int, variance: KmVariance ): KmTypeParameterVisitor? }. interface ElementVisitor<R, P> { R visitTypeParameter( TypeParameterElement e, P p ); }
  109. GsonTypeAdapter

  110. GsonTypeAdapter @GsonAdapter data class User( val id: Long, val username:

    String, val isAdmin: Boolean )
  111. val annotation = GsonAdapter::class.java for (element in roundEnv.getElementsAnnotatedWith(annotation)) { val

    input = getInputFrom(element) ?: continue if (!input.generateAndWrite()) return true }
  112. val annotation = GsonAdapter::class.java for (element in roundEnv.getElementsAnnotatedWith(annotation)) { val

    input = getInputFrom(element) ?: continue if (!input.generateAndWrite()) return true }
  113. val metadata = getMetadata(element)

  114. fun getMetadata(element: Element): KotlinClassMetadata? { val annotation = element.getAnnotation(Metadata::class.java) val

    header = annotation.run { KotlinClassHeader(k, mv, bv, d1, d2, xs, pn, xi) }. return KotlinClassMetadata.read(header) }.
  115. fun getMetadata(element: Element): KotlinClassMetadata? { val annotation = element.getAnnotation(Metadata::class.java) val

    header = annotation.run { KotlinClassHeader(k, mv, bv, d1, d2, xs, pn, xi) }. return KotlinClassMetadata.read(header) }.
  116. fun getMetadata(element: Element): KotlinClassMetadata? { val annotation = element.getAnnotation(Metadata::class.java) val

    header = annotation.run { KotlinClassHeader(k, mv, bv, d1, d2, xs, pn, xi) }. return KotlinClassMetadata.read(header) }.
  117. fun getMetadata(element: Element): KotlinClassMetadata? { val annotation = element.getAnnotation(Metadata::class.java) val

    header = annotation.run { KotlinClassHeader(k, mv, bv, d1, d2, xs, pn, xi) }. return KotlinClassMetadata.read(header) }.
  118. sealed class KotlinClassMetadata { class Class : KotlinClassMetadata class FileFacade

    : KotlinClassMetadata class SyntheticClass : KotlinClassMetadata class MultiFileClassFacade : KotlinClassMetadata class MultiFileClassPart : KotlinClassMetadata class Unknown : KotlinClassMetadata }
  119. sealed class KotlinClassMetadata { class Class : KotlinClassMetadata class FileFacade

    : KotlinClassMetadata class SyntheticClass : KotlinClassMetadata class MultiFileClassFacade : KotlinClassMetadata class MultiFileClassPart : KotlinClassMetadata class Unknown : KotlinClassMetadata }
  120. val metadata = getMetadata(element)

  121. val metadata = getMetadata(element) if (metadata !is KotlinClassMetadata.Class) { errorMustBeKotlinClass(element)

    return null }.
  122. val metadata = getMetadata(element) if (metadata !is KotlinClassMetadata.Class) { errorMustBeKotlinClass(element)

    return null }. metadata.accept(object : KmClassVisitor() { })
  123. val metadata = getMetadata(element) if (metadata !is KotlinClassMetadata.Class) { errorMustBeKotlinClass(element)

    return null }. metadata.accept(object : KmClassVisitor() { override fun visit(flags: Flags, name: ClassName) { } })
  124. val metadata = getMetadata(element) if (metadata !is KotlinClassMetadata.Class) { errorMustBeKotlinClass(element)

    return null }. metadata.accept(object : KmClassVisitor() { override fun visit(flags: Flags, name: ClassName) { pkg = name.substringBeforeLast('/').fqName() className = name.substringAfterLast('/') fqClassName = name.fqName() } })
  125. return null }. metadata.accept(object : KmClassVisitor() { override fun visit(flags:

    Flags, name: ClassName) { pkg = name.substringBeforeLast('/').fqName() className = name.substringAfterLast('/') fqClassName = name.fqName() } override fun visitConstructor(flags: Flags): KmConstructorVisitor? { } })
  126. return null }. metadata.accept(object : KmClassVisitor() { override fun visit(flags:

    Flags, name: ClassName) { pkg = name.substringBeforeLast('/').fqName() className = name.substringAfterLast('/') fqClassName = name.fqName() } override fun visitConstructor(flags: Flags): KmConstructorVisitor? { if (!flags.isPrimaryConstructor) return null } })
  127. val Flags.isPrimaryConstructor: Boolean get() = Flag.Constructor.IS_PRIMARY(this)

  128. val Flags.isNullableType: Boolean get() = Flag.Type.IS_NULLABLE(this) val Flags.isDataClass: Boolean get()

    = Flag.Class.IS_DATA(this) val Flags.isPrimaryConstructor: Boolean get() = Flag.Constructor.IS_PRIMARY(this) val Flags.hasDefaultValue: Boolean get() = Flag.ValueParameter.DECLARES_DEFAULT_VALUE(this)
  129. return null }. metadata.accept(object : KmClassVisitor() { override fun visit(flags:

    Flags, name: ClassName) { pkg = name.substringBeforeLast('/').fqName() className = name.substringAfterLast('/') fqClassName = name.fqName() } override fun visitConstructor(flags: Flags): KmConstructorVisitor? { if (!flags.isPrimaryConstructor) return null } })
  130. pkg = name.substringBeforeLast('/').fqName() className = name.substringAfterLast('/') fqClassName = name.fqName() }

    override fun visitConstructor(flags: Flags): KmConstructorVisitor? { if (!flags.isPrimaryConstructor) return null return object : KmConstructorVisitor() { override fun visitValueParameter( flags: Flags, name: String ): KmValueParameterVisitor? { }. }. } })
  131. } override fun visitConstructor(flags: Flags): KmConstructorVisitor? { if (!flags.isPrimaryConstructor) return

    null return object : KmConstructorVisitor() { override fun visitValueParameter( flags: Flags, name: String ): KmValueParameterVisitor? { return object : KmValueParameterVisitor() { override fun visitType(flags: Flags): KmTypeVisitor? { return ... } } }. }. } })
  132. return object :.KmTypeVisitor().{. }.

  133. return object :.KmTypeVisitor().{. override fun visitAbbreviatedType(flags: Flags): KmTypeVisitor? { }

    override fun visitArgument( flags: Flags, variance: KmVariance ): KmTypeVisitor? { } }.
  134. class KmTypeInfoVisitor() :.KmTypeVisitor().{. }.

  135. class KmTypeInfoVisitor( private val flags: Flags, private val onVisitEnd: (KmTypeInfoVisitor)

    -> Unit ) : KmTypeVisitor() {. }.
  136. class KmTypeInfoVisitor( private val flags: Flags, private val onVisitEnd: (KmTypeInfoVisitor)

    -> Unit ) : KmTypeVisitor() {. lateinit var fqName: String var isNullable: Boolean = false private val typeArgs = arrayListOf<String>() }.
  137. class KmTypeInfoVisitor( private val flags: Flags, private val onVisitEnd: (KmTypeInfoVisitor)

    -> Unit ) : KmTypeVisitor() { lateinit var fqName: String var isNullable: Boolean = false private val typeArgs = arrayListOf<String>() override fun visitEnd() { } }.
  138. private val onVisitEnd: (KmTypeInfoVisitor) -> Unit ) : KmTypeVisitor() {

    lateinit var fqName: String var isNullable: Boolean = false private val typeArgs = arrayListOf<String>() override fun visitEnd() { if (typeArgs.isNotEmpty()) { fqName += typeArgs.joinToString(prefix = "<", postfix = ">") typeArgs.clear() }, isNullable = flags.isNullableType onVisitEnd(this) } }.
  139. fqName += typeArgs.joinToString(prefix = "<", postfix = ">") typeArgs.clear() },

    isNullable = flags.isNullableType onVisitEnd(this) } override fun visitClass(name: ClassName) { fqName = name.fqName() }. }.
  140. onVisitEnd(this) } override fun visitClass(name: ClassName) { fqName = name.fqName()

    }. override fun visitAbbreviatedType(flags: Flags): KmTypeVisitor? { return KmTypeInfoVisitor(flags, typeResolver) { fqName = it.fqName typeArgs.clear() }. }, }.
  141. onVisitEnd(this) } override fun visitClass(name: ClassName) { fqName = name.fqName()

    }. override fun visitAbbreviatedType(flags: Flags): KmTypeVisitor? { return KmTypeInfoVisitor(flags, typeResolver) { fqName = it.fqName typeArgs.clear() }. }, }.
  142. fqName = name.fqName() }. override fun visitAbbreviatedType(flags: Flags): KmTypeVisitor? {

    return KmTypeInfoVisitor(flags, typeResolver) { fqName = it.fqName typeArgs.clear() }. },’ override fun visitTypeAlias(name: ClassName) { fqName = name.fqName() }. }.
  143. return KmTypeInfoVisitor(flags, typeResolver) { fqName = it.fqName typeArgs.clear() }. },

    override fun visitTypeAlias(name: ClassName) { fqName = name.fqName() }. override fun visitTypeParameter(id: Int) { fqName = typeResolver(id) ?: throw error("Missing param: id=$id") }, }.
  144. override fun visitTypeAlias(name: ClassName) { fqName = name.fqName() }. override

    fun visitTypeParameter(id: Int) { fqName = typeResolver(id) ?: throw error("Missing param: id=$id") }, override fun visitArgument( flags: Flags, variance: KmVariance ): KmTypeVisitor? { return KmTypeInfoVisitor(flags, typeResolver) { typeArgs += it.fqName }, }, }.
  145. override fun visitTypeAlias(name: ClassName) { fqName = name.fqName() }. override

    fun visitTypeParameter(id: Int) { fqName = typeResolver(id) ?: throw error("Missing param: id=$id") }, override fun visitArgument( flags: Flags, variance: KmVariance ): KmTypeVisitor? { return KmTypeInfoVisitor(flags, typeResolver) { typeArgs += it.fqName },. }, }.
  146. } override fun visitConstructor(flags: Flags): KmConstructorVisitor? { if (!flags.isPrimaryConstructor) return

    null return object : KmConstructorVisitor() { override fun visitValueParameter( flags: Flags, name: String ): KmValueParameterVisitor? { return object : KmValueParameterVisitor() { override fun visitType(flags: Flags): KmTypeVisitor? { return ... }. }. }. }. } })
  147. } override fun visitConstructor(flags: Flags): KmConstructorVisitor? { if (!flags.isPrimaryConstructor) return

    null return object : KmConstructorVisitor() { override fun visitValueParameter( flags: Flags, name: String ): KmValueParameterVisitor? { return object : KmValueParameterVisitor() { override fun visitType(flags: Flags): KmTypeVisitor? { return KmTypeInfoVisitor(flags, typeArgsRegistry::get) { parameters += Parameter(name, it.fqName, it.isNullable) } }. }. }. }. } })
  148. SHOW ME THE CODE

  149. Worth noting

  150. Worth noting • Metadata has only partial info

  151. Worth noting • Metadata has only partial info • Separate

    code model from generation
  152. Worth noting • Metadata has only partial info • Separate

    code model from generation • Both Kotlin & Java can be supported in one place
  153. Worth noting • Metadata has only partial info • Separate

    code model from generation • Both Kotlin & Java can be supported in one place • Multiplatform support will follow
  154. Other applications?

  155. Other applications? • Annotation processing with codegen

  156. Other applications? • Annotation processing with codegen • Bytecode weaving

  157. Other applications? • Annotation processing with codegen • Bytecode weaving

    • Code validation
  158. Other applications? • Annotation processing with codegen • Bytecode weaving

    • Code validation • Generating headers for interop with other languages: TypeScript, C/C++
  159. Links • Metadata ProtoBuffer spec: github.com/JetBrains/kotlin/blob/master/core/metadata/src/metadata.proto • kotlin-metadata by Eugenio

    Marletti: github.com/Takhion/kotlin-metadata • kotlinx-metadata by JetBrains: github.com/JetBrains/kotlin/tree/master/libraries/kotlinx-metadata/jvm • Value-based API for kotlinx-metadata: youtrack.jetbrains.com/issue/KT-26602 • Gson TypeAdapters codegen: github.com/colriot/GsonKotgen • KotlinPoet: github.com/square/kotlinpoet • Annotation Processing in a Kotlin World by Zac Sweers: youtu.be/_yaaCtWF8aE?t=18968 • Annotation Processing Boilerplate Destruction by Jake Wharton: youtu.be/dOcs-NKK-RA • Multiple KAPT rounds demo: github.com/Takhion/generate-kotlin-multiple-rounds
  160. Kotlin-friendly Annotation Processing Sergey Ryabov @colriot