1 /*
2 * 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("KotlinPoetMetadata")
17
18 package com.squareup.kotlinpoet.metadata
19
20 import javax.lang.model.element.TypeElement
21 import kotlin.annotation.AnnotationTarget.CLASS
22 import kotlin.annotation.AnnotationTarget.FUNCTION
23 import kotlin.annotation.AnnotationTarget.PROPERTY
24 import kotlin.reflect.KClass
25 import kotlinx.metadata.KmClass
26 import kotlinx.metadata.jvm.KotlinClassMetadata
27 import kotlinx.metadata.jvm.Metadata
28
29 /**
30 * Indicates that a given API is part of the experimental KotlinPoet metadata support. This exists
31 * because kotlinx-metadata is not a stable API, and will remain in place until it is.
32 */
33 @RequiresOptIn
34 @Retention(AnnotationRetention.BINARY)
35 @Target(CLASS, FUNCTION, PROPERTY)
36 public annotation class KotlinPoetMetadataPreview
37
38 /** @return a new [KmClass] representation of the Kotlin metadata for [this] class. */
39 @KotlinPoetMetadataPreview
toKmClassnull40 public fun KClass<*>.toKmClass(): KmClass = java.toKmClass()
41
42 /** @return a new [KmClass] representation of the Kotlin metadata for [this] class. */
43 @KotlinPoetMetadataPreview
44 public fun Class<*>.toKmClass(): KmClass = readMetadata(::getAnnotation).toKmClass()
45
46 /** @return a new [KmClass] representation of the Kotlin metadata for [this] type. */
47 @KotlinPoetMetadataPreview
48 public fun TypeElement.toKmClass(): KmClass = readMetadata(::getAnnotation).toKmClass()
49
50 @KotlinPoetMetadataPreview
51 public fun Metadata.toKmClass(): KmClass {
52 return toKotlinClassMetadata<KotlinClassMetadata.Class>()
53 .kmClass
54 }
55
56 @KotlinPoetMetadataPreview
toKotlinClassMetadatanull57 public inline fun <reified T : KotlinClassMetadata> Metadata.toKotlinClassMetadata(): T {
58 val expectedType = T::class
59 val metadata = readKotlinClassMetadata()
60 return when (expectedType) {
61 KotlinClassMetadata.Class::class -> {
62 check(metadata is KotlinClassMetadata.Class)
63 metadata as T
64 }
65 KotlinClassMetadata.FileFacade::class -> {
66 check(metadata is KotlinClassMetadata.FileFacade)
67 metadata as T
68 }
69 KotlinClassMetadata.SyntheticClass::class ->
70 throw UnsupportedOperationException("SyntheticClass isn't supported yet!")
71 KotlinClassMetadata.MultiFileClassFacade::class ->
72 throw UnsupportedOperationException("MultiFileClassFacade isn't supported yet!")
73 KotlinClassMetadata.MultiFileClassPart::class ->
74 throw UnsupportedOperationException("MultiFileClassPart isn't supported yet!")
75 KotlinClassMetadata.Unknown::class ->
76 throw RuntimeException("Recorded unknown metadata type! $metadata")
77 else -> TODO("Unrecognized KotlinClassMetadata type: $expectedType")
78 }
79 }
80
81 /**
82 * Returns the [KotlinClassMetadata] this represents. In general you should only use this function
83 * when you don't know what the underlying [KotlinClassMetadata] subtype is, otherwise you should
84 * use one of the more direct functions like [toKmClass].
85 */
86 @KotlinPoetMetadataPreview
readKotlinClassMetadatanull87 public fun Metadata.readKotlinClassMetadata(): KotlinClassMetadata {
88 val metadata = KotlinClassMetadata.read(asClassHeader())
89 checkNotNull(metadata) {
90 "Could not parse metadata! Try bumping kotlinpoet and/or kotlinx-metadata version."
91 }
92 return metadata
93 }
94
readMetadatanull95 private inline fun readMetadata(lookup: ((Class<Metadata>) -> Metadata?)): Metadata {
96 return checkNotNull(lookup.invoke(Metadata::class.java)) {
97 "No Metadata annotation found! Must be Kotlin code built with the standard library on the classpath."
98 }
99 }
100
asClassHeadernull101 private fun Metadata.asClassHeader(): Metadata {
102 return Metadata(
103 kind = kind,
104 metadataVersion = metadataVersion,
105 data1 = data1,
106 data2 = data2,
107 extraString = extraString,
108 packageName = packageName,
109 extraInt = extraInt,
110 )
111 }
112