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