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