1 /*
2  * Copyright (C) 2021 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 package com.squareup.kotlinpoet.metadata.classinspectors
17 
18 import javax.lang.model.element.Element
19 import javax.lang.model.element.ExecutableElement
20 import javax.lang.model.element.NestingKind
21 import javax.lang.model.element.QualifiedNameable
22 import javax.lang.model.element.TypeElement
23 import javax.lang.model.element.VariableElement
24 import javax.lang.model.type.ArrayType
25 import javax.lang.model.type.DeclaredType
26 import javax.lang.model.type.ErrorType
27 import javax.lang.model.type.ExecutableType
28 import javax.lang.model.type.IntersectionType
29 import javax.lang.model.type.NoType
30 import javax.lang.model.type.NullType
31 import javax.lang.model.type.PrimitiveType
32 import javax.lang.model.type.TypeKind.BOOLEAN
33 import javax.lang.model.type.TypeKind.BYTE
34 import javax.lang.model.type.TypeKind.CHAR
35 import javax.lang.model.type.TypeKind.DOUBLE
36 import javax.lang.model.type.TypeKind.FLOAT
37 import javax.lang.model.type.TypeKind.INT
38 import javax.lang.model.type.TypeKind.LONG
39 import javax.lang.model.type.TypeKind.SHORT
40 import javax.lang.model.type.TypeMirror
41 import javax.lang.model.type.TypeVariable
42 import javax.lang.model.type.UnionType
43 import javax.lang.model.type.WildcardType
44 import javax.lang.model.util.AbstractTypeVisitor8
45 import javax.lang.model.util.Types
46 import kotlin.metadata.jvm.JvmFieldSignature
47 import kotlin.metadata.jvm.JvmMethodSignature
48 
49 /*
50  * Adapted from
51  * - https://github.com/Takhion/kotlin-metadata/blob/e6de126575ad6ca10b093129b7c30d000c9b0c37/lib/src/main/kotlin/me/eugeniomarletti/kotlin/metadata/jvm/JvmDescriptorUtils.kt
52  * - https://github.com/Takhion/kotlin-metadata/pull/13
53  */
54 
55 /**
56  * For reference, see the [JVM specification, section 4.2](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2).
57  *
58  * @return the name of this [Element] in its "internal form".
59  */
60 @Suppress("RecursivePropertyAccessor")
61 internal val Element.internalName: String
62   get() = when (this) {
63     is TypeElement -> {
64       when (nestingKind) {
65         NestingKind.TOP_LEVEL ->
66           qualifiedName.toString().replace('.', '/')
67         NestingKind.MEMBER ->
68           enclosingElement.internalName + "$" + simpleName
69         NestingKind.LOCAL, NestingKind.ANONYMOUS ->
70           error("Unsupported nesting $nestingKind")
71         null ->
72           error("Unsupported, nestingKind == null")
73       }
74     }
75     is QualifiedNameable -> qualifiedName.toString().replace('.', '/')
76     else -> simpleName.toString()
77   }
78 
79 /**
80  * @return the "field descriptor" of this type.
81  * @see [JvmDescriptorTypeVisitor]
82  */
83 @Suppress("unused")
84 internal val NoType.descriptor: String
85   get() = "V"
86 
87 /**
88  * @return the "field descriptor" of this type.
89  * @see [JvmDescriptorTypeVisitor]
90  */
91 internal val DeclaredType.descriptor: String
92   get() = "L" + asElement().internalName + ";"
93 
94 /**
95  * @return the "field descriptor" of this type.
96  * @see [JvmDescriptorTypeVisitor]
97  */
98 internal val PrimitiveType.descriptor: String
99   get() = when (this.kind) {
100     BYTE -> "B"
101     CHAR -> "C"
102     DOUBLE -> "D"
103     FLOAT -> "F"
104     INT -> "I"
105     LONG -> "J"
106     SHORT -> "S"
107     BOOLEAN -> "Z"
108     else -> error("Unknown primitive type $this")
109   }
110 
111 /**
112  * @see [JvmDescriptorTypeVisitor]
113  */
descriptornull114 internal fun TypeMirror.descriptor(types: Types): String =
115   accept(JvmDescriptorTypeVisitor, types)
116 
117 /**
118  * @return the "field descriptor" of this type.
119  * @see [JvmDescriptorTypeVisitor]
120  */
121 internal fun WildcardType.descriptor(types: Types): String =
122   types.erasure(this).descriptor(types)
123 
124 /**
125  * @return the "field descriptor" of this type.
126  * @see [JvmDescriptorTypeVisitor]
127  */
128 internal fun TypeVariable.descriptor(types: Types): String =
129   types.erasure(this).descriptor(types)
130 
131 /**
132  * @return the "field descriptor" of this type.
133  * @see [JvmDescriptorTypeVisitor]
134  */
135 internal fun ArrayType.descriptor(types: Types): String =
136   "[" + componentType.descriptor(types)
137 
138 /**
139  * @return the "method descriptor" of this type.
140  * @see [JvmDescriptorTypeVisitor]
141  */
142 internal fun ExecutableType.descriptor(types: Types): String {
143   val parameterDescriptors = parameterTypes.joinToString(separator = "") { it.descriptor(types) }
144   val returnDescriptor = returnType.descriptor(types)
145   return "($parameterDescriptors)$returnDescriptor"
146 }
147 
148 /**
149  * Returns the JVM signature in the form "$Name$MethodDescriptor", for example: `equals(Ljava/lang/Object;)Z`.
150  *
151  * Useful for comparing with [JvmMethodSignature].
152  *
153  * For reference, see the [JVM specification, section 4.3](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3).
154  */
jvmMethodSignaturenull155 internal fun ExecutableElement.jvmMethodSignature(types: Types): String {
156   return "$simpleName${asType().descriptor(types)}"
157 }
158 
159 /**
160  * Returns the JVM signature in the form "$Name:$FieldDescriptor", for example: `"value:Ljava/lang/String;"`.
161  *
162  * Useful for comparing with [JvmFieldSignature].
163  *
164  * For reference, see the [JVM specification, section 4.3](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3).
165  */
jvmFieldSignaturenull166 internal fun VariableElement.jvmFieldSignature(types: Types): String {
167   return "$simpleName:${asType().descriptor(types)}"
168 }
169 
170 /**
171  * When applied over a type, it returns either:
172  * - a "field descriptor", for example: `Ljava/lang/Object;`
173  * - a "method descriptor", for example: `(Ljava/lang/Object;)Z`
174  *
175  * For reference, see the [JVM specification, section 4.3](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3).
176  */
177 internal object JvmDescriptorTypeVisitor : AbstractTypeVisitor8<String, Types>() {
visitNoTypenull178   override fun visitNoType(t: NoType, types: Types): String = t.descriptor
179   override fun visitDeclared(t: DeclaredType, types: Types): String = t.descriptor
180   override fun visitPrimitive(t: PrimitiveType, types: Types): String = t.descriptor
181 
182   override fun visitArray(t: ArrayType, types: Types): String = t.descriptor(types)
183   override fun visitWildcard(t: WildcardType, types: Types): String = t.descriptor(types)
184   override fun visitExecutable(t: ExecutableType, types: Types): String = t.descriptor(types)
185   override fun visitTypeVariable(t: TypeVariable, types: Types): String = t.descriptor(types)
186 
187   override fun visitNull(t: NullType, types: Types): String = visitUnknown(t, types)
188 
189   override fun visitError(t: ErrorType, types: Types): String = visitUnknown(t, types)
190 
191   override fun visitUnion(t: UnionType, types: Types): String = visitUnknown(t, types)
192 
193   override fun visitIntersection(t: IntersectionType, types: Types): String = visitUnknown(t, types)
194 
195   override fun visitUnknown(t: TypeMirror, types: Types): String = error("Unsupported type $t")
196 }
197