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.DelicateKotlinPoetApi
26 import com.squareup.kotlinpoet.FileSpec
27 import com.squareup.kotlinpoet.FunSpec
28 import com.squareup.kotlinpoet.FunSpec.Builder
29 import com.squareup.kotlinpoet.KModifier
30 import com.squareup.kotlinpoet.KModifier.ABSTRACT
31 import com.squareup.kotlinpoet.KModifier.CONST
32 import com.squareup.kotlinpoet.KModifier.CROSSINLINE
33 import com.squareup.kotlinpoet.KModifier.DATA
34 import com.squareup.kotlinpoet.KModifier.EXPECT
35 import com.squareup.kotlinpoet.KModifier.EXTERNAL
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.LATEINIT
40 import com.squareup.kotlinpoet.KModifier.NOINLINE
41 import com.squareup.kotlinpoet.KModifier.OPERATOR
42 import com.squareup.kotlinpoet.KModifier.PRIVATE
43 import com.squareup.kotlinpoet.KModifier.PUBLIC
44 import com.squareup.kotlinpoet.KModifier.SUSPEND
45 import com.squareup.kotlinpoet.KModifier.TAILREC
46 import com.squareup.kotlinpoet.KModifier.VALUE
47 import com.squareup.kotlinpoet.KModifier.VARARG
48 import com.squareup.kotlinpoet.MemberName
49 import com.squareup.kotlinpoet.ParameterSpec
50 import com.squareup.kotlinpoet.PropertySpec
51 import com.squareup.kotlinpoet.TypeAliasSpec
52 import com.squareup.kotlinpoet.TypeSpec
53 import com.squareup.kotlinpoet.UNIT
54 import com.squareup.kotlinpoet.asClassName
55 import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil
56 import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil.createAnnotations
57 import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil.createClassName
58 import com.squareup.kotlinpoet.metadata.classinspectors.ClassInspectorUtil.toTreeSet
59 import com.squareup.kotlinpoet.metadata.isAnnotation
60 import com.squareup.kotlinpoet.metadata.isClass
61 import com.squareup.kotlinpoet.metadata.isCompanionObject
62 import com.squareup.kotlinpoet.metadata.isDeclaration
63 import com.squareup.kotlinpoet.metadata.isEnum
64 import com.squareup.kotlinpoet.metadata.isInterface
65 import com.squareup.kotlinpoet.metadata.isObject
66 import com.squareup.kotlinpoet.metadata.isPrimary
67 import com.squareup.kotlinpoet.metadata.isVal
68 import com.squareup.kotlinpoet.metadata.specs.JvmMethodModifier.DEFAULT
69 import com.squareup.kotlinpoet.metadata.toKModifier
70 import com.squareup.kotlinpoet.metadata.toKmClass
71 import com.squareup.kotlinpoet.tag
72 import java.util.Locale
73 import javax.lang.model.element.Element
74 import javax.lang.model.element.ElementKind
75 import javax.lang.model.element.PackageElement
76 import javax.lang.model.element.TypeElement
77 import kotlin.metadata.ClassKind
78 import kotlin.metadata.KmClass
79 import kotlin.metadata.KmClassifier
80 import kotlin.metadata.KmConstructor
81 import kotlin.metadata.KmFunction
82 import kotlin.metadata.KmPackage
83 import kotlin.metadata.KmProperty
84 import kotlin.metadata.KmPropertyAccessorAttributes
85 import kotlin.metadata.KmType
86 import kotlin.metadata.KmTypeAlias
87 import kotlin.metadata.KmValueParameter
88 import kotlin.metadata.Modality
89 import kotlin.metadata.Visibility
90 import kotlin.metadata.declaresDefaultValue
91 import kotlin.metadata.hasAnnotations
92 import kotlin.metadata.isConst
93 import kotlin.metadata.isCrossinline
94 import kotlin.metadata.isData
95 import kotlin.metadata.isDelegated
96 import kotlin.metadata.isExpect
97 import kotlin.metadata.isExternal
98 import kotlin.metadata.isFunInterface
99 import kotlin.metadata.isInfix
100 import kotlin.metadata.isInline
101 import kotlin.metadata.isInner
102 import kotlin.metadata.isLateinit
103 import kotlin.metadata.isNoinline
104 import kotlin.metadata.isOperator
105 import kotlin.metadata.isReified
106 import kotlin.metadata.isSuspend
107 import kotlin.metadata.isTailrec
108 import kotlin.metadata.isValue
109 import kotlin.metadata.isVar
110 import kotlin.metadata.jvm.JvmMethodSignature
111 import kotlin.metadata.jvm.getterSignature
112 import kotlin.metadata.jvm.setterSignature
113 import kotlin.metadata.jvm.signature
114 import kotlin.metadata.jvm.toJvmInternalName
115 import kotlin.metadata.kind
116 import kotlin.metadata.modality
117 import kotlin.metadata.visibility
118 import kotlin.reflect.KClass
119 
120 /**
121  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
122  * @return a [TypeSpec] ABI representation of this [KClass].
123  */
124 public fun KClass<*>.toTypeSpec(
125   lenient: Boolean,
126   classInspector: ClassInspector? = null,
127 ): TypeSpec = java.toTypeSpec(lenient, classInspector)
128 
129 /**
130  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
131  * @return a [TypeSpec] ABI representation of this [KClass].
132  */
133 @OptIn(DelicateKotlinPoetApi::class)
134 public fun Class<*>.toTypeSpec(
135   lenient: Boolean,
136   classInspector: ClassInspector? = null,
137 ): TypeSpec = toKmClass(lenient).toTypeSpec(classInspector, asClassName())
138 
139 /**
140  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
141  * @return a [TypeSpec] ABI representation of this [TypeElement].
142  */
143 @OptIn(DelicateKotlinPoetApi::class)
144 public fun TypeElement.toTypeSpec(
145   lenient: Boolean,
146   classInspector: ClassInspector? = null,
147 ): TypeSpec = toKmClass(lenient).toTypeSpec(classInspector, asClassName())
148 
149 /**
150  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
151  * @return a [FileSpec] ABI representation of this [KClass].
152  */
153 public fun KClass<*>.toFileSpec(
154   lenient: Boolean,
155   classInspector: ClassInspector? = null,
156 ): FileSpec = java.toFileSpec(lenient, classInspector)
157 
158 /**
159  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
160  * @return a [FileSpec] ABI representation of this [KClass].
161  */
162 public fun Class<*>.toFileSpec(
163   lenient: Boolean,
164   classInspector: ClassInspector? = null,
165 ): FileSpec = FileSpec.get(`package`.name, toTypeSpec(lenient, classInspector))
166 
167 /**
168  * @param lenient see docs on [KotlinClassMetadata.readStrict] and [KotlinClassMetadata.readLenient] for more details.
169  * @return a [FileSpec] ABI representation of this [TypeElement].
170  */
171 public fun TypeElement.toFileSpec(
172   lenient: Boolean,
173   classInspector: ClassInspector? = null,
174 ): FileSpec = FileSpec.get(
175   packageName = packageName,
176   typeSpec = toTypeSpec(lenient, classInspector),
177 )
178 
179 /** @return a [TypeSpec] ABI representation of this [KmClass]. */
180 public fun KmClass.toTypeSpec(
181   classInspector: ClassInspector?,
182   className: ClassName = createClassName(name),
183 ): TypeSpec {
184   return toTypeSpec(classInspector, className, null)
185 }
186 
187 /** @return a [FileSpec] ABI representation of this [KmClass]. */
toFileSpecnull188 public fun KmClass.toFileSpec(
189   classInspector: ClassInspector?,
190   className: ClassName = createClassName(name),
191 ): FileSpec {
192   return FileSpec.get(
193     packageName = className.packageName,
194     typeSpec = toTypeSpec(classInspector, className),
195   )
196 }
197 
198 /** @return a [FileSpec] ABI representation of this [KmPackage]. */
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 
KmClassnull256 private fun KmClass.toTypeSpec(
257   classInspector: ClassInspector?,
258   className: ClassName,
259   parentClassName: ClassName?,
260 ): TypeSpec {
261   val classTypeParamsResolver = typeParameters.toTypeParameterResolver()
262   val jvmInternalName = name.toJvmInternalName()
263   val simpleName = className.simpleName
264   val classData = classInspector?.containerData(className, parentClassName)
265   check(classData is ClassData?) {
266     "Unexpected container data type: ${classData?.javaClass}"
267   }
268 
269   val builder = when {
270     isAnnotation -> TypeSpec.annotationBuilder(simpleName)
271     isCompanionObject -> TypeSpec.companionObjectBuilder(companionObjectName(simpleName))
272     isEnum -> TypeSpec.enumBuilder(simpleName)
273     isExpect -> TypeSpec.classBuilder(simpleName).addModifiers(EXPECT)
274     isObject -> TypeSpec.objectBuilder(simpleName)
275     isInterface -> {
276       if (classData?.declarationContainer?.isFunInterface == true) {
277         TypeSpec.funInterfaceBuilder(simpleName)
278       } else {
279         TypeSpec.interfaceBuilder(simpleName)
280       }
281     }
282 
283     kind == ClassKind.ENUM_ENTRY -> 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 (kind == ClassKind.ENUM_CLASS) {
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 (kind != ClassKind.ENUM_ENTRY) {
317     visibilityFrom(visibility) { builder.addModifiers(it) }
318     modality
319       .takeUnless { it == Modality.FINAL } // Default
320       ?.takeUnless { kind == ClassKind.INTERFACE && it == Modality.ABSTRACT } // Abstract is a default on interfaces
321       ?.let(Modality::toKModifier)
322       ?.let { builder.addModifiers(it) }
323     if (isData) {
324       builder.addModifiers(DATA)
325     }
326     if (isExternal) {
327       builder.addModifiers(EXTERNAL)
328     }
329     if (isValue) {
330       builder.addModifiers(VALUE)
331     }
332     if (isInner) {
333       builder.addModifiers(INNER)
334     }
335     builder.addTypeVariables(typeParameters.map { it.toTypeVariableName(classTypeParamsResolver) })
336     // If we have an inspector, we can check exactly which "supertype" is an interface vs
337     // class. Without a handler though, we have to best-effort guess. Usually, the flow is:
338     // - First element of a non-interface type is the superclass (can be `Any`)
339     // - First element of an interface type is the first superinterface
340     val superClassFilter = classInspector?.let { handler ->
341       { type: KmType ->
342         !handler.isInterface(createClassName((type.classifier as KmClassifier.Class).name))
343       }
344     } ?: { true }
345     val superClass = supertypes.asSequence()
346       .filter { it.classifier is KmClassifier.Class }
347       .find(superClassFilter)
348     if (superClass != null && !isEnum && !isInterface && !isAnnotation) {
349       superClass.toTypeName(classTypeParamsResolver).takeIf { it != ANY }
350         ?.let(builder::superclass)
351     }
352     builder.addSuperinterfaces(
353       supertypes.asSequence()
354         .filterNot { it == superClass }
355         .map { it.toTypeName(classTypeParamsResolver) }
356         .filterNot { it == ANY }
357         .asIterable(),
358     )
359     val primaryConstructorParams = mutableMapOf<String, ParameterSpec>()
360     if (isClass || isAnnotation || isEnum) {
361       primaryConstructor?.let {
362         it.toFunSpec(classTypeParamsResolver, classData?.constructors?.get(it) ?: return@let)
363           .also { spec ->
364             val finalSpec = if (isEnum && spec.annotations.isEmpty()) {
365               // Metadata specifies the constructor as private, but that's implicit so we can omit it
366               spec.toBuilder().apply { modifiers.remove(PRIVATE) }.build()
367             } else {
368               spec
369             }
370             builder.primaryConstructor(finalSpec)
371             primaryConstructorParams.putAll(spec.parameters.associateBy { it.name })
372           }
373       }
374       constructors.filter { !it.isPrimary }.takeIf { it.isNotEmpty() }?.let { secondaryConstructors ->
375         builder.addFunctions(
376           secondaryConstructors
377             .mapNotNull { kmConstructor ->
378               classData?.constructors?.get(kmConstructor)?.let { kmConstructor to it }
379             }
380             .map { (kmConstructor, constructorData) ->
381               kmConstructor.toFunSpec(classTypeParamsResolver, constructorData)
382             },
383         )
384       }
385     }
386     builder.addProperties(
387       properties
388         .asSequence()
389         .filter { it.kind.isDeclaration }
390         .map { it to classData?.properties?.get(it) }
391         .map { (property, propertyData) ->
392           property.toPropertySpec(
393             typeParamResolver = classTypeParamsResolver,
394             isConstructorParam = property.name in primaryConstructorParams,
395             classInspector = classInspector,
396             containerData = classData,
397             propertyData = propertyData,
398           )
399         }
400         .asIterable(),
401     )
402     companionObject?.let { objectName ->
403       val companionType = if (classInspector != null) {
404         val companionClassName = className.nestedClass(objectName)
405         classInspector.classFor(companionClassName)
406           .toTypeSpec(classInspector, companionClassName, parentClassName = className)
407       } else {
408         TypeSpec.companionObjectBuilder(companionObjectName(objectName))
409           .addKdoc(
410             "No ClassInspector was available during metadata parsing, so this companion object's API/contents may not be reflected accurately.",
411           )
412           .build()
413       }
414       builder.addType(companionType)
415     }
416   }
417   builder.addFunctions(
418     functions
419       .asSequence()
420       .filter { it.kind.isDeclaration }
421       .map { it to classData?.methods?.get(it) }
422       .map { (func, methodData) ->
423         func.toFunSpec(classTypeParamsResolver, classInspector, classData, methodData)
424           .toBuilder()
425           .apply {
426             // For interface methods, remove any body and mark the methods as abstract
427             fun isKotlinDefaultInterfaceMethod(): Boolean {
428               classInspector?.let { handler ->
429                 func.signature?.let { signature ->
430                   val suffix = signature.descriptor.removePrefix("(")
431                   return handler.methodExists(
432                     className.nestedClass("DefaultImpls"),
433                     signature.copy(
434                       descriptor = "(L$jvmInternalName;$suffix",
435                     ),
436                   )
437                 }
438               }
439               return false
440             }
441             // For interface methods, remove any body and mark the methods as abstract
442             // IFF it doesn't have a default interface body.
443             if (isInterface &&
444               annotations.none { it.typeName == JVM_DEFAULT } &&
445               (methodData?.jvmModifiers?.contains(DEFAULT) == false) &&
446               !isKotlinDefaultInterfaceMethod()
447             ) {
448               addModifiers(ABSTRACT)
449               clearBody()
450             } else if (ABSTRACT in modifiers) {
451               // Remove bodies for abstract functions
452               clearBody()
453             }
454             if (methodData?.isSynthetic == true) {
455               addKdoc(
456                 "Note: Since this is a synthetic function, some JVM information " +
457                   "(annotations, modifiers) may be missing.",
458               )
459             }
460           }
461           .build()
462       }
463       .asIterable(),
464   )
465 
466   for (it in nestedClasses) {
467     val nestedClassName = className.nestedClass(it)
468     val nestedClass = classInspector?.classFor(nestedClassName)
469     val nestedType = if (nestedClass != null) {
470       if (nestedClass.isCompanionObject) {
471         // We handle these separately
472         continue
473       } else {
474         nestedClass.toTypeSpec(classInspector, nestedClassName, parentClassName = className)
475       }
476     } else {
477       TypeSpec.classBuilder(it)
478         .addKdoc(
479           "No ClassInspector was available during metadata parsing, so this nested class's API/contents may not be reflected accurately.",
480         )
481         .build()
482     }
483     builder.addType(nestedType)
484   }
485 
486   return builder
487     .tag(this)
488     .build()
489 }
490 
companionObjectNamenull491 private fun companionObjectName(name: String): String? {
492   return if (name == "Companion") null else name
493 }
494 
KmConstructornull495 private fun KmConstructor.toFunSpec(
496   typeParamResolver: TypeParameterResolver,
497   constructorData: ConstructorData?,
498 ): FunSpec {
499   return FunSpec.constructorBuilder()
500     .apply {
501       addAnnotations(constructorData?.allAnnotations.orEmpty())
502       visibilityFrom(visibility) { addModifiers(it) }
503       addParameters(
504         this@toFunSpec.valueParameters.mapIndexed { index, param ->
505           param.toParameterSpec(
506             typeParamResolver,
507             constructorData?.takeIf { it != ConstructorData.EMPTY }
508               ?.parameterAnnotations
509               ?.get(index)
510               .orEmpty(),
511           )
512         },
513       )
514       if (!this@toFunSpec.isPrimary) {
515         // TODO How do we know when to add callSuperConstructor()?
516       }
517     }
518     .tag(this)
519     .build()
520 }
521 
522 private val ContainerData.isInterface: Boolean get() {
containernull523   return declarationContainer.let { container ->
524     container is KmClass && container.isInterface
525   }
526 }
527 
toFunSpecnull528 private fun KmFunction.toFunSpec(
529   classTypeParamsResolver: TypeParameterResolver = TypeParameterResolver.EMPTY,
530   classInspector: ClassInspector? = null,
531   containerData: ContainerData? = null,
532   methodData: MethodData? = null,
533   isInInterface: Boolean = containerData?.isInterface ?: false,
534 ): FunSpec {
535   val typeParamsResolver = typeParameters.toTypeParameterResolver(
536     fallback = classTypeParamsResolver,
537   )
538   val mutableAnnotations = mutableListOf<AnnotationSpec>()
539   if (classInspector != null && containerData != null) {
540     signature?.let { signature ->
541       if (!containerData.isInterface) {
542         // Infer if JvmName was used
543         // We skip interface types for this because they can't have @JvmName.
544         signature.jvmNameAnnotation(name)?.let { jvmNameAnnotation ->
545           mutableAnnotations += jvmNameAnnotation
546         }
547       }
548     }
549   }
550   val anyReified = typeParameters.any { it.isReified }
551   val isInFacade = containerData is FileData
552   val annotations = mutableAnnotations
553     .plus(methodData?.allAnnotations(containsReifiedTypeParameter = anyReified).orEmpty())
554     .filterNot { isInFacade && it.typeName == JVM_STATIC }
555     .toTreeSet()
556   return FunSpec.builder(name)
557     .apply {
558       addAnnotations(annotations)
559       visibilityFrom(visibility) { addModifiers(it) }
560       val isOverride = methodData?.isOverride == true
561       modality
562         .takeUnless { it == Modality.FINAL && !isOverride } // Final is the default
563         ?.takeUnless { it == Modality.OPEN && isOverride } // Overrides are implicitly open
564         ?.takeUnless { it == Modality.OPEN && isInInterface } // interface methods are implicitly open
565         ?.let(Modality::toKModifier)
566         ?.let { addModifiers(it) }
567       if (valueParameters.isNotEmpty()) {
568         addParameters(
569           valueParameters.mapIndexed { index, param ->
570             param.toParameterSpec(
571               typeParamsResolver,
572               // This can be empty if the element is synthetic
573               methodData?.parameterAnnotations?.get(index).orEmpty(),
574             )
575           },
576         )
577       }
578       if (typeParameters.isNotEmpty()) {
579         addTypeVariables(typeParameters.map { it.toTypeVariableName(typeParamsResolver) })
580       }
581       if (methodData?.isOverride == true) {
582         addModifiers(KModifier.OVERRIDE)
583       }
584       if (isOperator) {
585         addModifiers(OPERATOR)
586       }
587       if (isInfix) {
588         addModifiers(INFIX)
589       }
590       if (isInline) {
591         addModifiers(INLINE)
592       }
593       if (isTailrec) {
594         addModifiers(TAILREC)
595       }
596       if (isExternal) {
597         addModifiers(EXTERNAL)
598       }
599       if (isExpect) {
600         addModifiers(EXPECT)
601       }
602       if (isSuspend) {
603         addModifiers(SUSPEND)
604       }
605       val returnTypeName = this@toFunSpec.returnType.toTypeName(typeParamsResolver)
606       if (returnTypeName != UNIT) {
607         returns(returnTypeName)
608         if (modality != Modality.ABSTRACT) {
609           addStatement(NOT_IMPLEMENTED)
610         }
611       }
612       receiverParameterType?.toTypeName(typeParamsResolver)?.let { receiver(it) }
613     }
614     .tag(this)
615     .build()
616 }
617 
KmValueParameternull618 private fun KmValueParameter.toParameterSpec(
619   typeParamResolver: TypeParameterResolver,
620   annotations: Collection<AnnotationSpec>,
621 ): ParameterSpec {
622   val paramType = varargElementType ?: type
623   return ParameterSpec.builder(name, paramType.toTypeName(typeParamResolver))
624     .apply {
625       addAnnotations(annotations)
626       if (varargElementType != null) {
627         addModifiers(VARARG)
628       }
629       if (isCrossinline) {
630         addModifiers(CROSSINLINE)
631       }
632       if (isNoinline) {
633         addModifiers(NOINLINE)
634       }
635       if (declaresDefaultValue) {
636         defaultValue(NOT_IMPLEMENTED)
637       }
638     }
639     .tag(this)
640     .build()
641 }
642 
KmPropertynull643 private fun KmProperty.toPropertySpec(
644   typeParamResolver: TypeParameterResolver = TypeParameterResolver.EMPTY,
645   isConstructorParam: Boolean = false,
646   classInspector: ClassInspector? = null,
647   containerData: ContainerData? = null,
648   propertyData: PropertyData? = null,
649   isInInterface: Boolean = containerData?.isInterface ?: false,
650 ): PropertySpec {
651   val isOverride = propertyData?.isOverride ?: false
652   val returnTypeName = returnType.toTypeName(typeParamResolver)
653   val mutableAnnotations = mutableListOf<AnnotationSpec>()
654   if (containerData != null && propertyData != null) {
655     getterSignature?.let { getterSignature ->
656       if (!containerData.isInterface &&
657         modality != Modality.OPEN && modality != Modality.ABSTRACT
658       ) {
659         // Infer if JvmName was used
660         // We skip interface types or open/abstract properties because they can't have @JvmName.
661         // For annotation properties, kotlinc puts JvmName annotations by default in
662         // bytecode but they're implicit in source, so we expect the simple name for
663         // annotation types.
664         val expectedMetadataName = if (containerData is ClassData &&
665           containerData.declarationContainer.isAnnotation
666         ) {
667           name
668         } else {
669           "get${name.safeCapitalize(Locale.US)}"
670         }
671         getterSignature.jvmNameAnnotation(
672           metadataName = expectedMetadataName,
673           useSiteTarget = UseSiteTarget.GET,
674         )?.let { jvmNameAnnotation ->
675           mutableAnnotations += jvmNameAnnotation
676         }
677       }
678     }
679     if (setter != null) {
680       setterSignature?.let { setterSignature ->
681         if (containerData is ClassData &&
682           !containerData.declarationContainer.isAnnotation &&
683           !containerData.declarationContainer.isInterface &&
684           classInspector?.supportsNonRuntimeRetainedAnnotations == false &&
685           modality != Modality.OPEN && modality != Modality.ABSTRACT
686         ) {
687           // Infer if JvmName was used
688           // We skip annotation types for this because they can't have vars.
689           // We skip interface types or open/abstract properties because they can't have @JvmName.
690           setterSignature.jvmNameAnnotation(
691             metadataName = "set${name.safeCapitalize(Locale.US)}",
692             useSiteTarget = UseSiteTarget.SET,
693           )?.let { jvmNameAnnotation ->
694             mutableAnnotations += jvmNameAnnotation
695           }
696         }
697       }
698     }
699   }
700   return PropertySpec.builder(name, returnTypeName)
701     .apply {
702       // If a property annotation doesn't have a custom site target and is used in a constructor
703       // we have to add the property: site target to it.
704 
705       val isInFacade = containerData is FileData
706       val finalAnnotations = mutableAnnotations
707         .plus(propertyData?.allAnnotations.orEmpty())
708         .filterNot { (isConst || isInFacade) && it.typeName == JVM_STATIC }
709         .map {
710           if (isConstructorParam && it.useSiteTarget == null) {
711             // TODO Ideally don't do this if the annotation use site is only field?
712             //  e.g. JvmField. It's technically fine, but redundant on parameters as it's
713             //  automatically applied to the property for these annotation types.
714             //  This is another thing ClassInspector *could* tell us
715             it.toBuilder().useSiteTarget(UseSiteTarget.PROPERTY).build()
716           } else {
717             it
718           }
719         }
720         .toTreeSet()
721       addAnnotations(finalAnnotations)
722       visibilityFrom(visibility) { addModifiers(it) }
723       modality
724         .takeUnless { it == Modality.FINAL && !isOverride } // Final is the default
725         ?.takeUnless { it == Modality.OPEN && isOverride } // Overrides are implicitly open
726         ?.takeUnless { it == Modality.OPEN && isInInterface } // Interface properties implicitly open
727         ?.takeUnless { it == Modality.ABSTRACT && isInInterface } // Interface properties implicitly abstract
728         ?.let(Modality::toKModifier)
729         ?.let { addModifiers(it) }
730       if (isOverride) {
731         addModifiers(KModifier.OVERRIDE)
732       }
733       if (isConst) {
734         addModifiers(CONST)
735       }
736       if (isVar) {
737         mutable(true)
738       } else if (isVal) {
739         mutable(false)
740       }
741       if (isDelegated) {
742         // Placeholders for these are tricky
743         addKdoc("Note: delegation is ABI stub only and not guaranteed to match source code.")
744         if (isVal) {
745           delegate("%M { %L }", MemberName("kotlin", "lazy"), NOT_IMPLEMENTED) // Placeholder
746         } else {
747           if (returnTypeName.isNullable) {
748             delegate(
749               "%T.observable(null) { _, _, _ -> }",
750               ClassName("kotlin.properties", "Delegates"),
751             )
752           } else {
753             delegate("%T.notNull()", ClassName("kotlin.properties", "Delegates")) // Placeholder
754           }
755         }
756       }
757       if (isExpect) {
758         addModifiers(EXPECT)
759       }
760       if (isExternal) {
761         addModifiers(EXTERNAL)
762       }
763       if (isLateinit) {
764         addModifiers(LATEINIT)
765       }
766       if (isConstructorParam || (!isDelegated && !isLateinit)) {
767         val constant = propertyData?.fieldData?.constant
768         when {
769           constant != null -> initializer(constant)
770           isConstructorParam -> initializer(name)
771           returnTypeName.isNullable -> initializer("null")
772           modality == Modality.ABSTRACT || isInInterface -> {
773             // No-op, don't emit an initializer for abstract or interface properties
774           }
775           else -> initializer(NOT_IMPLEMENTED)
776         }
777       }
778       // Delegated properties have setters/getters defined for some reason, ignore here
779       // since the delegate handles it
780       // vals with initialized constants have a getter in bytecode but not a body in kotlin source
781       val modifierSet = modifiers.toSet()
782       if (!isDelegated && modality != Modality.ABSTRACT) {
783         propertyAccessor(
784           modifierSet,
785           this@toPropertySpec.getter,
786           FunSpec.getterBuilder().addStatement(NOT_IMPLEMENTED),
787           isOverride,
788         )?.let(::getter)
789       }
790       if (setter != null && !isDelegated && modality != Modality.ABSTRACT) {
791         propertyAccessor(modifierSet, setter!!, FunSpec.setterBuilder(), isOverride)?.let(::setter)
792       }
793     }
794     .tag(this)
795     .build()
796 }
797 
propertyAccessornull798 private fun propertyAccessor(
799   propertyModifiers: Set<KModifier>,
800   attrs: KmPropertyAccessorAttributes,
801   functionBuilder: Builder,
802   isOverride: Boolean,
803 ): FunSpec? {
804   val visibility = attrs.visibility.toKModifier()
805   if (visibility == PUBLIC || visibility !in propertyModifiers) {
806     // This is redundant and just a stub
807     // For annotations on this accessor, we declare them on the property with site target instead
808     return null
809   }
810   val modality = attrs.modality
811     .takeUnless { it == Modality.FINAL && !isOverride }
812     .takeUnless { it == Modality.OPEN && isOverride }
813 
814   val localModifiers = buildList {
815     if (attrs.isExternal) {
816       add(EXTERNAL)
817     }
818     if (attrs.isInline) {
819       add(INLINE)
820     }
821   }
822   return if (modality != null || localModifiers.isNotEmpty()) {
823     functionBuilder
824       .apply {
825         addModifiers(visibility)
826         modality?.let { addModifiers(it.toKModifier()) }
827         addModifiers(localModifiers)
828       }
829       .build()
830   } else {
831     null
832   }
833 }
834 
toTypeAliasSpecnull835 private fun KmTypeAlias.toTypeAliasSpec(): TypeAliasSpec {
836   val typeParamResolver = typeParameters.toTypeParameterResolver()
837   return TypeAliasSpec.builder(name, underlyingType.toTypeName(typeParamResolver))
838     .apply {
839       visibilityFrom(visibility) {
840         addModifiers(it)
841       }
842       if (hasAnnotations) {
843         val annotationSpecs = this@toTypeAliasSpec.annotations
844           .map { it.toAnnotationSpec() }
845         addAnnotations(annotationSpecs)
846       }
847     }
848     .addTypeVariables(typeParamResolver.parametersMap.values)
849     .build()
850 }
851 
JvmMethodSignaturenull852 private fun JvmMethodSignature.jvmNameAnnotation(
853   metadataName: String,
854   useSiteTarget: UseSiteTarget? = null,
855 ): AnnotationSpec? {
856   return if (name == metadataName) {
857     null
858   } else {
859     return AnnotationSpec.builder(JvmName::class)
860       .addMember("name = %S", name)
861       .useSiteTarget(useSiteTarget)
862       .build()
863   }
864 }
865 
866 private val JAVA_ANNOTATION_ANNOTATIONS = setOf(
867   java.lang.annotation.Retention::class.asClassName(),
868   java.lang.annotation.Target::class.asClassName(),
869 )
870 
visibilityFromnull871 private fun visibilityFrom(visibility: Visibility, body: (KModifier) -> Unit) {
872   val modifierVisibility = visibility.toKModifier()
873   if (modifierVisibility != PUBLIC) {
874     body(modifierVisibility)
875   }
876 }
877 
safeCapitalizenull878 private fun String.safeCapitalize(locale: Locale): String {
879   return replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() }
880 }
881 
882 private val METADATA = Metadata::class.asClassName()
883 
884 private val JVM_DEFAULT = ClassName("kotlin.jvm", "JvmDefault")
885 private val JVM_STATIC = JvmStatic::class.asClassName()
886 
887 @PublishedApi
888 internal val Element.packageName: String
889   get() {
890     var element = this
891     while (element.kind != ElementKind.PACKAGE) {
892       element = element.enclosingElement
893     }
894     return (element as PackageElement).toString()
895   }
896