• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2019 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 @file:JvmName("KotlinPoetMetadataSpecs")
17 
18 package com.squareup.kotlinpoet.metadata.specs
19 
20 import com.squareup.kotlinpoet.ANY
21 import com.squareup.kotlinpoet.AnnotationSpec
22 import com.squareup.kotlinpoet.AnnotationSpec.UseSiteTarget
23 import com.squareup.kotlinpoet.AnnotationSpec.UseSiteTarget.FILE
24 import com.squareup.kotlinpoet.ClassName
25 import com.squareup.kotlinpoet.FileSpec
26 import com.squareup.kotlinpoet.FunSpec
27 import com.squareup.kotlinpoet.FunSpec.Builder
28 import com.squareup.kotlinpoet.KModifier
29 import com.squareup.kotlinpoet.KModifier.ABSTRACT
30 import com.squareup.kotlinpoet.KModifier.CONST
31 import com.squareup.kotlinpoet.KModifier.CROSSINLINE
32 import com.squareup.kotlinpoet.KModifier.DATA
33 import com.squareup.kotlinpoet.KModifier.EXPECT
34 import com.squareup.kotlinpoet.KModifier.EXTERNAL
35 import com.squareup.kotlinpoet.KModifier.FINAL
36 import com.squareup.kotlinpoet.KModifier.INFIX
37 import com.squareup.kotlinpoet.KModifier.INLINE
38 import com.squareup.kotlinpoet.KModifier.INNER
39 import com.squareup.kotlinpoet.KModifier.INTERNAL
40 import com.squareup.kotlinpoet.KModifier.LATEINIT
41 import com.squareup.kotlinpoet.KModifier.NOINLINE
42 import com.squareup.kotlinpoet.KModifier.OPEN
43 import com.squareup.kotlinpoet.KModifier.OPERATOR
44 import com.squareup.kotlinpoet.KModifier.PRIVATE
45 import com.squareup.kotlinpoet.KModifier.PROTECTED
46 import com.squareup.kotlinpoet.KModifier.PUBLIC
47 import com.squareup.kotlinpoet.KModifier.SEALED
48 import com.squareup.kotlinpoet.KModifier.SUSPEND
49 import com.squareup.kotlinpoet.KModifier.TAILREC
50 import com.squareup.kotlinpoet.KModifier.VALUE
51 import com.squareup.kotlinpoet.KModifier.VARARG
52 import com.squareup.kotlinpoet.MemberName
53 import com.squareup.kotlinpoet.ParameterSpec
54 import com.squareup.kotlinpoet.PropertySpec
55 import com.squareup.kotlinpoet.TypeAliasSpec
56 import com.squareup.kotlinpoet.TypeSpec
57 import com.squareup.kotlinpoet.UNIT
58 import com.squareup.kotlinpoet.asClassName
59 import com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview
60 import com.squareup.kotlinpoet.metadata.PropertyAccessorFlag
61 import com.squareup.kotlinpoet.metadata.PropertyAccessorFlag.IS_EXTERNAL
62 import com.squareup.kotlinpoet.metadata.PropertyAccessorFlag.IS_INLINE
63 import com.squareup.kotlinpoet.metadata.PropertyAccessorFlag.IS_NOT_DEFAULT
64 import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil
65 import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil.createAnnotations
66 import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil.createClassName
67 import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil.toTreeSet
68 import com.squareup.kotlinpoet.metadata.declaresDefaultValue
69 import com.squareup.kotlinpoet.metadata.hasAnnotations
70 import com.squareup.kotlinpoet.metadata.hasGetter
71 import com.squareup.kotlinpoet.metadata.hasSetter
72 import com.squareup.kotlinpoet.metadata.isAbstract
73 import com.squareup.kotlinpoet.metadata.isAnnotation
74 import com.squareup.kotlinpoet.metadata.isClass
75 import com.squareup.kotlinpoet.metadata.isCompanionObject
76 import com.squareup.kotlinpoet.metadata.isConst
77 import com.squareup.kotlinpoet.metadata.isCrossInline
78 import com.squareup.kotlinpoet.metadata.isData
79 import com.squareup.kotlinpoet.metadata.isDeclaration
80 import com.squareup.kotlinpoet.metadata.isDelegated
81 import com.squareup.kotlinpoet.metadata.isDelegation
82 import com.squareup.kotlinpoet.metadata.isEnum
83 import com.squareup.kotlinpoet.metadata.isEnumEntry
84 import com.squareup.kotlinpoet.metadata.isExpect
85 import com.squareup.kotlinpoet.metadata.isExternal
86 import com.squareup.kotlinpoet.metadata.isFinal
87 import com.squareup.kotlinpoet.metadata.isFun
88 import com.squareup.kotlinpoet.metadata.isInfix
89 import com.squareup.kotlinpoet.metadata.isInline
90 import com.squareup.kotlinpoet.metadata.isInner
91 import com.squareup.kotlinpoet.metadata.isInterface
92 import com.squareup.kotlinpoet.metadata.isInternal
93 import com.squareup.kotlinpoet.metadata.isLateinit
94 import com.squareup.kotlinpoet.metadata.isNoInline
95 import com.squareup.kotlinpoet.metadata.isObject
96 import com.squareup.kotlinpoet.metadata.isOpen
97 import com.squareup.kotlinpoet.metadata.isOperator
98 import com.squareup.kotlinpoet.metadata.isPrimary
99 import com.squareup.kotlinpoet.metadata.isPrivate
100 import com.squareup.kotlinpoet.metadata.isProtected
101 import com.squareup.kotlinpoet.metadata.isPublic
102 import com.squareup.kotlinpoet.metadata.isReified
103 import com.squareup.kotlinpoet.metadata.isSealed
104 import com.squareup.kotlinpoet.metadata.isSuspend
105 import com.squareup.kotlinpoet.metadata.isSynthesized
106 import com.squareup.kotlinpoet.metadata.isTailRec
107 import com.squareup.kotlinpoet.metadata.isVal
108 import com.squareup.kotlinpoet.metadata.isValue
109 import com.squareup.kotlinpoet.metadata.isVar
110 import com.squareup.kotlinpoet.metadata.propertyAccessorFlags
111 import com.squareup.kotlinpoet.metadata.specs.JvmMethodModifier.DEFAULT
112 import com.squareup.kotlinpoet.metadata.toKmClass
113 import com.squareup.kotlinpoet.tag
114 import java.util.Locale
115 import javax.lang.model.element.Element
116 import javax.lang.model.element.ElementKind
117 import javax.lang.model.element.PackageElement
118 import javax.lang.model.element.TypeElement
119 import kotlin.reflect.KClass
120 import kotlinx.metadata.Flags
121 import kotlinx.metadata.KmClass
122 import kotlinx.metadata.KmClassifier
123 import kotlinx.metadata.KmConstructor
124 import kotlinx.metadata.KmFunction
125 import kotlinx.metadata.KmPackage
126 import kotlinx.metadata.KmProperty
127 import kotlinx.metadata.KmType
128 import kotlinx.metadata.KmTypeAlias
129 import kotlinx.metadata.KmValueParameter
130 import kotlinx.metadata.jvm.JvmMethodSignature
131 import kotlinx.metadata.jvm.getterSignature
132 import kotlinx.metadata.jvm.jvmInternalName
133 import kotlinx.metadata.jvm.setterSignature
134 import kotlinx.metadata.jvm.signature
135 
136 /** @return a [TypeSpec] ABI representation of this [KClass]. */
137 @KotlinPoetMetadataPreview
138 public fun KClass<*>.toTypeSpec(
139   classInspector: ClassInspector? = null,
140 ): TypeSpec = java.toTypeSpec(classInspector)
141 
142 /** @return a [TypeSpec] ABI representation of this [KClass]. */
143 @KotlinPoetMetadataPreview
144 public fun Class<*>.toTypeSpec(
145   classInspector: ClassInspector? = null,
146 ): TypeSpec = toKmClass().toTypeSpec(classInspector, asClassName())
147 
148 /** @return a [TypeSpec] ABI representation of this [TypeElement]. */
149 @Suppress("DEPRECATION")
150 @KotlinPoetMetadataPreview
151 public fun TypeElement.toTypeSpec(
152   classInspector: ClassInspector? = null,
153 ): TypeSpec = toKmClass().toTypeSpec(classInspector, asClassName())
154 
155 /** @return a [FileSpec] ABI representation of this [KClass]. */
156 @KotlinPoetMetadataPreview
157 public fun KClass<*>.toFileSpec(
158   classInspector: ClassInspector? = null,
159 ): FileSpec = java.toFileSpec(classInspector)
160 
161 /** @return a [FileSpec] ABI representation of this [KClass]. */
162 @KotlinPoetMetadataPreview
163 public fun Class<*>.toFileSpec(
164   classInspector: ClassInspector? = null,
165 ): FileSpec = FileSpec.get(`package`.name, toTypeSpec(classInspector))
166 
167 /** @return a [FileSpec] ABI representation of this [TypeElement]. */
168 @KotlinPoetMetadataPreview
169 public fun TypeElement.toFileSpec(
170   classInspector: ClassInspector? = null,
171 ): FileSpec = FileSpec.get(
172   packageName = packageName,
173   typeSpec = toTypeSpec(classInspector),
174 )
175 
176 /** @return a [TypeSpec] ABI representation of this [KmClass]. */
177 @KotlinPoetMetadataPreview
178 public fun KmClass.toTypeSpec(
179   classInspector: ClassInspector?,
180   className: ClassName = createClassName(name),
181 ): TypeSpec {
182   return toTypeSpec(classInspector, className, null)
183 }
184 
185 /** @return a [FileSpec] ABI representation of this [KmClass]. */
186 @KotlinPoetMetadataPreview
toFileSpecnull187 public fun KmClass.toFileSpec(
188   classInspector: ClassInspector?,
189   className: ClassName = createClassName(name),
190 ): FileSpec {
191   return FileSpec.get(
192     packageName = className.packageName,
193     typeSpec = toTypeSpec(classInspector, className),
194   )
195 }
196 
197 /** @return a [FileSpec] ABI representation of this [KmPackage]. */
198 @KotlinPoetMetadataPreview
toFileSpecnull199 public fun KmPackage.toFileSpec(
200   classInspector: ClassInspector?,
201   className: ClassName,
202 ): FileSpec {
203   val fileData = classInspector?.containerData(className, null)
204   check(fileData is FileData?) {
205     "Unexpected container data type: ${fileData?.javaClass}"
206   }
207   val fileName = fileData?.fileName ?: className.simpleName
208   return FileSpec.builder(className.packageName, fileName)
209     .apply {
210       fileData?.let { data ->
211         data.jvmName?.let { name ->
212           addAnnotation(
213             AnnotationSpec.builder(ClassInspectorUtil.JVM_NAME)
214               .addMember("name = %S", name)
215               .build(),
216           )
217         }
218         val fileAnnotations = createAnnotations(FILE) {
219           addAll(data.annotations.filterNot { it.typeName == METADATA })
220         }
221         for (fileAnnotation in fileAnnotations) {
222           addAnnotation(fileAnnotation)
223         }
224       }
225       for (function in functions) {
226         val methodData = fileData?.methods?.get(function)
227         addFunction(
228           function.toFunSpec(
229             classInspector = classInspector,
230             containerData = fileData,
231             methodData = methodData,
232             isInInterface = false,
233           ),
234         )
235       }
236       for (property in properties) {
237         val propertyData = fileData?.properties?.get(property)
238         addProperty(
239           property.toPropertySpec(
240             classInspector = classInspector,
241             containerData = fileData,
242             propertyData = propertyData,
243             isInInterface = false,
244           ),
245         )
246       }
247       for (alias in typeAliases) {
248         addTypeAlias(alias.toTypeAliasSpec())
249       }
250     }
251     .build()
252 }
253 
254 private const val NOT_IMPLEMENTED = "throw·NotImplementedError(\"Stub!\")"
255 
256 @KotlinPoetMetadataPreview
KmClassnull257 private fun KmClass.toTypeSpec(
258   classInspector: ClassInspector?,
259   className: ClassName,
260   parentClassName: ClassName?,
261 ): TypeSpec {
262   val classTypeParamsResolver = typeParameters.toTypeParameterResolver()
263   val jvmInternalName = name.jvmInternalName
264   val simpleName = className.simpleName
265   val classData = classInspector?.containerData(className, parentClassName)
266   check(classData is ClassData?) {
267     "Unexpected container data type: ${classData?.javaClass}"
268   }
269 
270   val builder = when {
271     isAnnotation -> TypeSpec.annotationBuilder(simpleName)
272     isCompanionObject -> TypeSpec.companionObjectBuilder(companionObjectName(simpleName))
273     isEnum -> TypeSpec.enumBuilder(simpleName)
274     isExpect -> TypeSpec.expectClassBuilder(simpleName)
275     isObject -> TypeSpec.objectBuilder(simpleName)
276     isInterface -> {
277       if (classData?.declarationContainer?.isFun == true) {
278         TypeSpec.funInterfaceBuilder(simpleName)
279       } else {
280         TypeSpec.interfaceBuilder(simpleName)
281       }
282     }
283     isEnumEntry -> TypeSpec.anonymousClassBuilder()
284     else -> TypeSpec.classBuilder(simpleName)
285   }
286 
287   classData?.annotations
288     ?.filterNot {
289       it.typeName == METADATA || it.typeName in JAVA_ANNOTATION_ANNOTATIONS
290     }
291     ?.let(builder::addAnnotations)
292 
293   if (isEnum) {
294     enumEntries.forEach { entryName ->
295       val typeSpec = if (classInspector != null) {
296         val entry = classInspector.enumEntry(className, entryName)
297         entry.declarationContainer
298           ?.let { enumEntryClass ->
299             val entryClassName = className.nestedClass(entryName)
300             enumEntryClass.toTypeSpec(classInspector, entryClassName, parentClassName = className)
301           }
302           ?: TypeSpec.anonymousClassBuilder()
303             .addAnnotations(entry.annotations)
304             .build()
305       } else {
306         TypeSpec.anonymousClassBuilder()
307           .addKdoc(
308             "No ClassInspector was available during metadata parsing, so this entry may not be reflected accurately if it has a class body.",
309           )
310           .build()
311       }
312       builder.addEnumConstant(entryName, typeSpec)
313     }
314   }
315 
316   if (!isEnumEntry) {
317     visibilityFrom(flags) { builder.addModifiers(it) }
318     builder.addModifiers(
319       *flags.modalities
320         .filterNot { it == FINAL } // Default
321         .filterNot { isInterface && it == ABSTRACT } // Abstract is a default on interfaces
322         .toTypedArray(),
323     )
324     if (isData) {
325       builder.addModifiers(DATA)
326     }
327     if (isExternal) {
328       builder.addModifiers(EXTERNAL)
329     }
330     if (isValue) {
331       builder.addModifiers(VALUE)
332     }
333     if (isInner) {
334       builder.addModifiers(INNER)
335     }
336     builder.addTypeVariables(typeParameters.map { it.toTypeVariableName(classTypeParamsResolver) })
337     // If we have an inspector, we can check exactly which "supertype" is an interface vs
338     // class. Without a handler though, we have to best-effort guess. Usually, the flow is:
339     // - First element of a non-interface type is the superclass (can be `Any`)
340     // - First element of an interface type is the first superinterface
341     val superClassFilter = classInspector?.let { handler ->
342       { type: KmType ->
343         !handler.isInterface(createClassName((type.classifier as KmClassifier.Class).name))
344       }
345     } ?: { true }
346     val superClass = supertypes.asSequence()
347       .filter { it.classifier is KmClassifier.Class }
348       .find(superClassFilter)
349     if (superClass != null && !isEnum && !isInterface && !isAnnotation) {
350       superClass.toTypeName(classTypeParamsResolver).takeIf { it != ANY }
351         ?.let(builder::superclass)
352     }
353     builder.addSuperinterfaces(
354       supertypes.asSequence()
355         .filterNot { it == superClass }
356         .map { it.toTypeName(classTypeParamsResolver) }
357         .filterNot { it == ANY }
358         .asIterable(),
359     )
360     val primaryConstructorParams = mutableMapOf<String, ParameterSpec>()
361     if (isClass || isAnnotation || isEnum) {
362       primaryConstructor?.let {
363         it.toFunSpec(classTypeParamsResolver, classData?.constructors?.get(it) ?: return@let)
364           .also { spec ->
365             val finalSpec = if (isEnum && spec.annotations.isEmpty()) {
366               // Metadata specifies the constructor as private, but that's implicit so we can omit it
367               spec.toBuilder().apply { modifiers.remove(PRIVATE) }.build()
368             } else {
369               spec
370             }
371             builder.primaryConstructor(finalSpec)
372             primaryConstructorParams.putAll(spec.parameters.associateBy { it.name })
373           }
374       }
375       constructors.filter { !it.isPrimary }.takeIf { it.isNotEmpty() }?.let { secondaryConstructors ->
376         builder.addFunctions(
377           secondaryConstructors
378             .mapNotNull { kmConstructor ->
379               classData?.constructors?.get(kmConstructor)?.let { kmConstructor to it }
380             }
381             .map { (kmConstructor, constructorData) ->
382               kmConstructor.toFunSpec(classTypeParamsResolver, constructorData)
383             },
384         )
385       }
386     }
387     builder.addProperties(
388       properties
389         .asSequence()
390         .filter { it.isDeclaration }
391         .filterNot { it.isSynthesized }
392         .map { it to classData?.properties?.get(it) }
393         .map { (property, propertyData) ->
394           property.toPropertySpec(
395             typeParamResolver = classTypeParamsResolver,
396             isConstructorParam = property.name in primaryConstructorParams,
397             classInspector = classInspector,
398             containerData = classData,
399             propertyData = propertyData,
400           )
401         }
402         .asIterable(),
403     )
404     companionObject?.let { objectName ->
405       val companionType = if (classInspector != null) {
406         val companionClassName = className.nestedClass(objectName)
407         classInspector.classFor(companionClassName)
408           .toTypeSpec(classInspector, companionClassName, parentClassName = className)
409       } else {
410         TypeSpec.companionObjectBuilder(companionObjectName(objectName))
411           .addKdoc(
412             "No ClassInspector was available during metadata parsing, so this companion object's API/contents may not be reflected accurately.",
413           )
414           .build()
415       }
416       builder.addType(companionType)
417     }
418   }
419   builder.addFunctions(
420     functions
421       .asSequence()
422       .filter { it.isDeclaration }
423       .filterNot { it.isDelegation }
424       .filterNot { it.isSynthesized }
425       .map { it to classData?.methods?.get(it) }
426       .map { (func, methodData) ->
427         func.toFunSpec(classTypeParamsResolver, classInspector, classData, methodData)
428           .toBuilder()
429           .apply {
430             // For interface methods, remove any body and mark the methods as abstract
431             fun isKotlinDefaultInterfaceMethod(): Boolean {
432               classInspector?.let { handler ->
433                 func.signature?.let { signature ->
434                   val suffix = signature.desc.removePrefix("(")
435                   return handler.methodExists(
436                     className.nestedClass("DefaultImpls"),
437                     signature.copy(
438                       desc = "(L$jvmInternalName;$suffix",
439                     ),
440                   )
441                 }
442               }
443               return false
444             }
445             // For interface methods, remove any body and mark the methods as abstract
446             // IFF it doesn't have a default interface body.
447             if (isInterface &&
448               annotations.none { it.typeName == JVM_DEFAULT } &&
449               (methodData?.jvmModifiers?.contains(DEFAULT) == false) &&
450               !isKotlinDefaultInterfaceMethod()
451             ) {
452               addModifiers(ABSTRACT)
453               clearBody()
454             } else if (ABSTRACT in modifiers) {
455               // Remove bodies for abstract functions
456               clearBody()
457             }
458             if (methodData?.isSynthetic == true) {
459               addKdoc(
460                 "Note: Since this is a synthetic function, some JVM information " +
461                   "(annotations, modifiers) may be missing.",
462               )
463             }
464           }
465           .build()
466       }
467       .asIterable(),
468   )
469 
470   for (it in nestedClasses) {
471     val nestedClassName = className.nestedClass(it)
472     val nestedClass = classInspector?.classFor(nestedClassName)
473     val nestedType = if (nestedClass != null) {
474       if (nestedClass.isCompanionObject) {
475         // We handle these separately
476         continue
477       } else {
478         nestedClass.toTypeSpec(classInspector, nestedClassName, parentClassName = className)
479       }
480     } else {
481       TypeSpec.classBuilder(it)
482         .addKdoc(
483           "No ClassInspector was available during metadata parsing, so this nested class's API/contents may not be reflected accurately.",
484         )
485         .build()
486     }
487     builder.addType(nestedType)
488   }
489 
490   return builder
491     .tag(this)
492     .build()
493 }
494 
companionObjectNamenull495 private fun companionObjectName(name: String): String? {
496   return if (name == "Companion") null else name
497 }
498 
499 @KotlinPoetMetadataPreview
KmConstructornull500 private fun KmConstructor.toFunSpec(
501   typeParamResolver: TypeParameterResolver,
502   constructorData: ConstructorData?,
503 ): FunSpec {
504   return FunSpec.constructorBuilder()
505     .apply {
506       addAnnotations(constructorData?.allAnnotations.orEmpty())
507       visibilityFrom(flags) { addModifiers(it) }
508       addParameters(
509         this@toFunSpec.valueParameters.mapIndexed { index, param ->
510           param.toParameterSpec(
511             typeParamResolver,
512             constructorData?.takeIf { it != ConstructorData.EMPTY }
513               ?.parameterAnnotations
514               ?.get(index)
515               .orEmpty(),
516           )
517         },
518       )
519       if (!isPrimary) {
520         // TODO How do we know when to add callSuperConstructor()?
521       }
522     }
523     .tag(this)
524     .build()
525 }
526 
527 @KotlinPoetMetadataPreview
528 private val ContainerData.isInterface: Boolean get() {
containernull529   return declarationContainer.let { container ->
530     container is KmClass && container.isInterface
531   }
532 }
533 
534 @KotlinPoetMetadataPreview
toFunSpecnull535 private fun KmFunction.toFunSpec(
536   classTypeParamsResolver: TypeParameterResolver = TypeParameterResolver.EMPTY,
537   classInspector: ClassInspector? = null,
538   containerData: ContainerData? = null,
539   methodData: MethodData? = null,
540   isInInterface: Boolean = containerData?.isInterface ?: false,
541 ): FunSpec {
542   val typeParamsResolver = typeParameters.toTypeParameterResolver(
543     fallback = classTypeParamsResolver,
544   )
545   val mutableAnnotations = mutableListOf<AnnotationSpec>()
546   if (classInspector != null && containerData != null) {
547     signature?.let { signature ->
548       if (!containerData.isInterface) {
549         // Infer if JvmName was used
550         // We skip interface types for this because they can't have @JvmName.
551         signature.jvmNameAnnotation(name)?.let { jvmNameAnnotation ->
552           mutableAnnotations += jvmNameAnnotation
553         }
554       }
555     }
556   }
557   val anyReified = typeParameters.any { it.isReified }
558   val isInFacade = containerData is FileData
559   val annotations = mutableAnnotations
560     .plus(methodData?.allAnnotations(containsReifiedTypeParameter = anyReified).orEmpty())
561     .filterNot { isInFacade && it.typeName == JVM_STATIC }
562     .toTreeSet()
563   return FunSpec.builder(name)
564     .apply {
565       addAnnotations(annotations)
566       visibilityFrom(flags) { addModifiers(it) }
567       val isOverride = methodData?.isOverride == true
568       addModifiers(
569         flags.modalities
570           .filterNot { it == FINAL && !isOverride } // Final is the default
571           .filterNot { it == OPEN && isOverride } // Overrides are implicitly open
572           .filterNot { it == OPEN && isInInterface }, // interface methods are implicitly open
573       )
574       if (valueParameters.isNotEmpty()) {
575         addParameters(
576           valueParameters.mapIndexed { index, param ->
577             param.toParameterSpec(
578               typeParamsResolver,
579               // This can be empty if the element is synthetic
580               methodData?.parameterAnnotations?.get(index).orEmpty(),
581             )
582           },
583         )
584       }
585       if (typeParameters.isNotEmpty()) {
586         addTypeVariables(typeParameters.map { it.toTypeVariableName(typeParamsResolver) })
587       }
588       if (methodData?.isOverride == true) {
589         addModifiers(KModifier.OVERRIDE)
590       }
591       if (isOperator) {
592         addModifiers(OPERATOR)
593       }
594       if (isInfix) {
595         addModifiers(INFIX)
596       }
597       if (isInline) {
598         addModifiers(INLINE)
599       }
600       if (isTailRec) {
601         addModifiers(TAILREC)
602       }
603       if (isExternal) {
604         addModifiers(EXTERNAL)
605       }
606       if (isExpect) {
607         addModifiers(EXPECT)
608       }
609       if (isSuspend) {
610         addModifiers(SUSPEND)
611       }
612       val returnTypeName = this@toFunSpec.returnType.toTypeName(typeParamsResolver)
613       if (returnTypeName != UNIT) {
614         returns(returnTypeName)
615         if (!flags.isAbstract) {
616           addStatement(NOT_IMPLEMENTED)
617         }
618       }
619       receiverParameterType?.toTypeName(typeParamsResolver)?.let { receiver(it) }
620     }
621     .tag(this)
622     .build()
623 }
624 
625 @KotlinPoetMetadataPreview
KmValueParameternull626 private fun KmValueParameter.toParameterSpec(
627   typeParamResolver: TypeParameterResolver,
628   annotations: Collection<AnnotationSpec>,
629 ): ParameterSpec {
630   val paramType = varargElementType ?: type ?: throw IllegalStateException("No argument type!")
631   return ParameterSpec.builder(name, paramType.toTypeName(typeParamResolver))
632     .apply {
633       addAnnotations(annotations)
634       if (varargElementType != null) {
635         addModifiers(VARARG)
636       }
637       if (isCrossInline) {
638         addModifiers(CROSSINLINE)
639       }
640       if (isNoInline) {
641         addModifiers(NOINLINE)
642       }
643       if (declaresDefaultValue) {
644         defaultValue(NOT_IMPLEMENTED)
645       }
646     }
647     .tag(this)
648     .build()
649 }
650 
651 @KotlinPoetMetadataPreview
KmPropertynull652 private fun KmProperty.toPropertySpec(
653   typeParamResolver: TypeParameterResolver = TypeParameterResolver.EMPTY,
654   isConstructorParam: Boolean = false,
655   classInspector: ClassInspector? = null,
656   containerData: ContainerData? = null,
657   propertyData: PropertyData? = null,
658   isInInterface: Boolean = containerData?.isInterface ?: false,
659 ): PropertySpec {
660   val isOverride = propertyData?.isOverride ?: false
661   val returnTypeName = returnType.toTypeName(typeParamResolver)
662   val mutableAnnotations = mutableListOf<AnnotationSpec>()
663   if (containerData != null && propertyData != null) {
664     if (hasGetter) {
665       getterSignature?.let { getterSignature ->
666         if (!containerData.isInterface &&
667           !flags.isOpen &&
668           !flags.isAbstract
669         ) {
670           // Infer if JvmName was used
671           // We skip interface types or open/abstract properties because they can't have @JvmName.
672           // For annotation properties, kotlinc puts JvmName annotations by default in
673           // bytecode but they're implicit in source, so we expect the simple name for
674           // annotation types.
675           val expectedMetadataName = if (containerData is ClassData &&
676             containerData.declarationContainer.isAnnotation
677           ) {
678             name
679           } else {
680             "get${name.safeCapitalize(Locale.US)}"
681           }
682           getterSignature.jvmNameAnnotation(
683             metadataName = expectedMetadataName,
684             useSiteTarget = UseSiteTarget.GET,
685           )?.let { jvmNameAnnotation ->
686             mutableAnnotations += jvmNameAnnotation
687           }
688         }
689       }
690     }
691     if (hasSetter) {
692       setterSignature?.let { setterSignature ->
693         if (containerData is ClassData &&
694           !containerData.declarationContainer.isAnnotation &&
695           !containerData.declarationContainer.isInterface &&
696           classInspector?.supportsNonRuntimeRetainedAnnotations == false &&
697           !flags.isOpen &&
698           !flags.isAbstract
699         ) {
700           // Infer if JvmName was used
701           // We skip annotation types for this because they can't have vars.
702           // We skip interface types or open/abstract properties because they can't have @JvmName.
703           setterSignature.jvmNameAnnotation(
704             metadataName = "set${name.safeCapitalize(Locale.US)}",
705             useSiteTarget = UseSiteTarget.SET,
706           )?.let { jvmNameAnnotation ->
707             mutableAnnotations += jvmNameAnnotation
708           }
709         }
710       }
711     }
712   }
713   return PropertySpec.builder(name, returnTypeName)
714     .apply {
715       // If a property annotation doesn't have a custom site target and is used in a constructor
716       // we have to add the property: site target to it.
717 
718       val isInFacade = containerData is FileData
719       val finalAnnotations = mutableAnnotations
720         .plus(propertyData?.allAnnotations.orEmpty())
721         .filterNot { (isConst || isInFacade) && it.typeName == JVM_STATIC }
722         .map {
723           if (isConstructorParam && it.useSiteTarget == null) {
724             // TODO Ideally don't do this if the annotation use site is only field?
725             //  e.g. JvmField. It's technically fine, but redundant on parameters as it's
726             //  automatically applied to the property for these annotation types.
727             //  This is another thing ClassInspector *could* tell us
728             it.toBuilder().useSiteTarget(UseSiteTarget.PROPERTY).build()
729           } else {
730             it
731           }
732         }
733         .toTreeSet()
734       addAnnotations(finalAnnotations)
735       visibilityFrom(flags) { addModifiers(it) }
736       addModifiers(
737         flags.modalities
738           .filterNot { it == FINAL && !isOverride } // Final is the default
739           .filterNot { it == OPEN && isOverride } // Overrides are implicitly open
740           .filterNot { it == OPEN && isInInterface } // Interface properties implicitly open
741           .filterNot { it == ABSTRACT && isInInterface }, // Interface properties implicitly abstract
742       )
743       if (isOverride) {
744         addModifiers(KModifier.OVERRIDE)
745       }
746       if (isConst) {
747         addModifiers(CONST)
748       }
749       if (isVar) {
750         mutable(true)
751       } else if (isVal) {
752         mutable(false)
753       }
754       if (isDelegated) {
755         // Placeholders for these are tricky
756         addKdoc("Note: delegation is ABI stub only and not guaranteed to match source code.")
757         if (isVal) {
758           delegate("%M { %L }", MemberName("kotlin", "lazy"), NOT_IMPLEMENTED) // Placeholder
759         } else {
760           if (returnTypeName.isNullable) {
761             delegate(
762               "%T.observable(null) { _, _, _ -> }",
763               ClassName("kotlin.properties", "Delegates"),
764             )
765           } else {
766             delegate("%T.notNull()", ClassName("kotlin.properties", "Delegates")) // Placeholder
767           }
768         }
769       }
770       if (isExpect) {
771         addModifiers(EXPECT)
772       }
773       if (isExternal) {
774         addModifiers(EXTERNAL)
775       }
776       if (isLateinit) {
777         addModifiers(LATEINIT)
778       }
779       if (isConstructorParam || (!isDelegated && !isLateinit)) {
780         val constant = propertyData?.fieldData?.constant
781         when {
782           constant != null -> initializer(constant)
783           isConstructorParam -> initializer(name)
784           returnTypeName.isNullable -> initializer("null")
785           flags.isAbstract || isInInterface -> {
786             // No-op, don't emit an initializer for abstract or interface properties
787           }
788           else -> initializer(NOT_IMPLEMENTED)
789         }
790       }
791       // Delegated properties have setters/getters defined for some reason, ignore here
792       // since the delegate handles it
793       // vals with initialized constants have a getter in bytecode but not a body in kotlin source
794       val modifierSet = modifiers.toSet()
795       if (hasGetter && !isDelegated && !flags.isAbstract) {
796         propertyAccessor(
797           modifierSet,
798           getterFlags,
799           FunSpec.getterBuilder().addStatement(NOT_IMPLEMENTED),
800           isOverride,
801         )?.let(::getter)
802       }
803       if (hasSetter && !isDelegated && !flags.isAbstract) {
804         propertyAccessor(modifierSet, setterFlags, FunSpec.setterBuilder(), isOverride)?.let(::setter)
805       }
806     }
807     .tag(this)
808     .build()
809 }
810 
811 @KotlinPoetMetadataPreview
propertyAccessornull812 private fun propertyAccessor(
813   propertyModifiers: Set<KModifier>,
814   flags: Flags,
815   functionBuilder: Builder,
816   isOverride: Boolean,
817 ): FunSpec? {
818   val visibility = flags.visibility
819   if (visibility == PUBLIC || visibility !in propertyModifiers) {
820     // This is redundant and just a stub
821     // For annotations on this accessor, we declare them on the property with site target instead
822     return null
823   }
824   val modalities = flags.modalities
825     .filterNot { it == FINAL && !isOverride }
826     .filterNot { it == OPEN && isOverride }
827   val propertyAccessorFlags = flags.propertyAccessorFlags
828   return if (visibility != PUBLIC || modalities.isNotEmpty() || propertyAccessorFlags.isNotEmpty()) {
829     functionBuilder
830       .apply {
831         addModifiers(visibility)
832         addModifiers(modalities)
833         addModifiers(*propertyAccessorFlags.toKModifiersArray())
834       }
835       .build()
836   } else {
837     null
838   }
839 }
840 
toKModifiersArraynull841 private fun Set<PropertyAccessorFlag>.toKModifiersArray(): Array<KModifier> {
842   return mapNotNull {
843     when (it) {
844       IS_EXTERNAL -> EXTERNAL
845       IS_INLINE -> INLINE
846       IS_NOT_DEFAULT -> null // Gracefully skip over these
847     }
848   }.toTypedArray()
849 }
850 
851 @KotlinPoetMetadataPreview
toTypeAliasSpecnull852 private fun KmTypeAlias.toTypeAliasSpec(): TypeAliasSpec {
853   val typeParamResolver = typeParameters.toTypeParameterResolver()
854   return TypeAliasSpec.builder(name, underlyingType.toTypeName(typeParamResolver))
855     .apply {
856       visibilityFrom(flags) {
857         addModifiers(it)
858       }
859       if (flags.hasAnnotations) {
860         val annotationSpecs = this@toTypeAliasSpec.annotations
861           .map { it.toAnnotationSpec() }
862         addAnnotations(annotationSpecs)
863       }
864     }
865     .addTypeVariables(typeParamResolver.parametersMap.values)
866     .build()
867 }
868 
JvmMethodSignaturenull869 private fun JvmMethodSignature.jvmNameAnnotation(
870   metadataName: String,
871   useSiteTarget: UseSiteTarget? = null,
872 ): AnnotationSpec? {
873   return if (name == metadataName) {
874     null
875   } else {
876     return AnnotationSpec.builder(JvmName::class)
877       .addMember("name = %S", name)
878       .useSiteTarget(useSiteTarget)
879       .build()
880   }
881 }
882 
883 private val JAVA_ANNOTATION_ANNOTATIONS = setOf(
884   java.lang.annotation.Retention::class.asClassName(),
885   java.lang.annotation.Target::class.asClassName(),
886 )
887 
888 @KotlinPoetMetadataPreview
889 private val Flags.visibility: KModifier
890   get() = when {
891     isInternal -> INTERNAL
892     isPrivate -> PRIVATE
893     isProtected -> PROTECTED
894     isPublic -> PUBLIC
895     else -> {
896       // IS_PRIVATE_TO_THIS or IS_LOCAL, so just default to public
897       PUBLIC
898     }
899   }
900 
901 @KotlinPoetMetadataPreview
visibilityFromnull902 private fun visibilityFrom(flags: Flags, body: (KModifier) -> Unit) {
903   val modifierVisibility = flags.visibility
904   if (modifierVisibility != PUBLIC) {
905     body(modifierVisibility)
906   }
907 }
908 
safeCapitalizenull909 private fun String.safeCapitalize(locale: Locale): String {
910   return replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() }
911 }
912 
913 @KotlinPoetMetadataPreview
914 private val Flags.modalities: Set<KModifier>
<lambda>null915   get() = setOf {
916     if (isFinal) {
917       add(FINAL)
918     }
919     if (isOpen) {
920       add(OPEN)
921     }
922     if (isAbstract) {
923       add(ABSTRACT)
924     }
925     if (isSealed) {
926       add(SEALED)
927     }
928   }
929 
setOfnull930 private inline fun <E> setOf(body: MutableSet<E>.() -> Unit): Set<E> {
931   return mutableSetOf<E>().apply(body).toSet()
932 }
933 
934 private val METADATA = Metadata::class.asClassName()
935 
936 @Suppress("DEPRECATION")
937 private val JVM_DEFAULT = JvmDefault::class.asClassName()
938 private val JVM_STATIC = JvmStatic::class.asClassName()
939 
940 @PublishedApi
941 internal val Element.packageName: String
942   get() {
943     var element = this
944     while (element.kind != ElementKind.PACKAGE) {
945       element = element.enclosingElement
946     }
947     return (element as PackageElement).toString()
948   }
949