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