1 /*
<lambda>null2  * Copyright 2020 Google LLC
3  * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.google.devtools.ksp.symbol.impl.binary
19 
20 import com.google.devtools.ksp.ExceptionMessage
21 import com.google.devtools.ksp.KSObjectCache
22 import com.google.devtools.ksp.getClassDeclarationByName
23 import com.google.devtools.ksp.processing.impl.KSNameImpl
24 import com.google.devtools.ksp.processing.impl.ResolverImpl
25 import com.google.devtools.ksp.symbol.*
26 import com.google.devtools.ksp.symbol.impl.findPsi
27 import com.google.devtools.ksp.symbol.impl.java.KSAnnotationJavaImpl
28 import com.google.devtools.ksp.symbol.impl.kotlin.KSErrorType
29 import com.google.devtools.ksp.symbol.impl.kotlin.KSValueArgumentLiteImpl
30 import com.google.devtools.ksp.symbol.impl.kotlin.getKSTypeCached
31 import com.google.devtools.ksp.symbol.impl.synthetic.KSTypeReferenceSyntheticImpl
32 import com.intellij.psi.JavaPsiFacade
33 import com.intellij.psi.PsiAnnotation
34 import com.intellij.psi.PsiAnnotationMethod
35 import org.jetbrains.kotlin.builtins.StandardNames
36 import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
37 import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
38 import org.jetbrains.kotlin.descriptors.ClassDescriptor
39 import org.jetbrains.kotlin.descriptors.NotFoundClasses
40 import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
41 import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
42 import org.jetbrains.kotlin.load.java.components.JavaAnnotationDescriptor
43 import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaAnnotationDescriptor
44 import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
45 import org.jetbrains.kotlin.load.java.structure.*
46 import org.jetbrains.kotlin.load.java.structure.impl.VirtualFileBoundJavaClass
47 import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser
48 import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaAnnotationVisitor
49 import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaMethod
50 import org.jetbrains.kotlin.load.java.structure.impl.classFiles.ClassifierResolutionContext
51 import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass
52 import org.jetbrains.kotlin.load.kotlin.getContainingKotlinJvmBinaryClass
53 import org.jetbrains.kotlin.name.ClassId
54 import org.jetbrains.kotlin.name.FqName
55 import org.jetbrains.kotlin.psi.KtParameter
56 import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue
57 import org.jetbrains.kotlin.resolve.constants.*
58 import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
59 import org.jetbrains.kotlin.types.KotlinType
60 import org.jetbrains.kotlin.types.TypeConstructor
61 import org.jetbrains.kotlin.types.isError
62 import org.jetbrains.kotlin.types.typeUtil.builtIns
63 import org.jetbrains.org.objectweb.asm.AnnotationVisitor
64 import org.jetbrains.org.objectweb.asm.ClassReader
65 import org.jetbrains.org.objectweb.asm.ClassVisitor
66 import org.jetbrains.org.objectweb.asm.MethodVisitor
67 import org.jetbrains.org.objectweb.asm.Opcodes.API_VERSION
68 
69 class KSAnnotationDescriptorImpl private constructor(
70     val descriptor: AnnotationDescriptor,
71     override val parent: KSNode?
72 ) : KSAnnotation {
73     companion object : KSObjectCache<Pair<AnnotationDescriptor, KSNode?>, KSAnnotationDescriptorImpl>() {
74         fun getCached(descriptor: AnnotationDescriptor, parent: KSNode?) = cache.getOrPut(Pair(descriptor, parent)) {
75             KSAnnotationDescriptorImpl(descriptor, parent)
76         }
77     }
78 
79     override val origin =
80         when (descriptor) {
81             is JavaAnnotationDescriptor, is LazyJavaAnnotationDescriptor -> Origin.JAVA_LIB
82             else -> Origin.KOTLIN_LIB
83         }
84 
85     override val location: Location = NonExistLocation
86 
87     override val annotationType: KSTypeReference by lazy {
88         KSTypeReferenceDescriptorImpl.getCached(descriptor.type, origin, this)
89     }
90 
91     override val arguments: List<KSValueArgument> by lazy {
92         descriptor.createKSValueArguments(this)
93     }
94 
95     override val defaultArguments: List<KSValueArgument> by lazy {
96         descriptor.getDefaultArguments(this)
97     }
98 
99     override val shortName: KSName by lazy {
100         KSNameImpl.getCached(descriptor.fqName!!.shortName().asString())
101     }
102 
103     override val useSiteTarget: AnnotationUseSiteTarget? = null
104 
105     override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R {
106         return visitor.visitAnnotation(this, data)
107     }
108 
109     override fun toString(): String {
110         return "@${shortName.asString()}"
111     }
112 }
113 
ClassIdnull114 private fun ClassId.findKSClassDeclaration(): KSClassDeclaration? {
115     val ksName = KSNameImpl.getCached(this.asSingleFqName().asString().replace("$", "."))
116     return ResolverImpl.instance!!.getClassDeclarationByName(ksName)
117 }
118 
findKSTypenull119 private fun ClassId.findKSType(): KSType? = findKSClassDeclaration()?.asStarProjectedType()
120 
121 private fun <T> ConstantValue<T>.toValue(parent: KSNode): Any? = when (this) {
122     is AnnotationValue -> KSAnnotationDescriptorImpl.getCached(value, parent)
123     is ArrayValue -> value.map { it.toValue(parent) }
124     is EnumValue -> value.first.findKSClassDeclaration()?.declarations?.find {
125         it is KSClassDeclaration && it.classKind == ClassKind.ENUM_ENTRY &&
126             it.simpleName.asString() == value.second.asString()
127     }?.let { (it as KSClassDeclaration).asStarProjectedType() }
128     is KClassValue -> when (val classValue = value) {
129         is KClassValue.Value.NormalClass -> if (classValue.arrayDimensions > 0) {
130             classValue.value.classId.findKSType()?.let { componentType ->
131                 var resultingType = componentType
132                 for (i in 1..classValue.arrayDimensions) {
133                     resultingType = ResolverImpl.instance!!.builtIns.arrayType.replace(
134                         listOf(
135                             ResolverImpl.instance!!.getTypeArgument(
136                                 KSTypeReferenceSyntheticImpl.getCached(resultingType, null), Variance.INVARIANT
137                             )
138                         )
139                     )
140                 }
141                 resultingType
142             }
143         } else classValue.classId.findKSType()
144         is KClassValue.Value.LocalClass -> getKSTypeCached(classValue.type)
145     }
146     is ErrorValue, is NullValue -> null
147     else -> value
148 }
149 
AnnotationDescriptornull150 fun AnnotationDescriptor.createKSValueArguments(ownerAnnotation: KSAnnotation): List<KSValueArgument> {
151     val presentValueArguments = allValueArguments.map { (name, constantValue) ->
152         KSValueArgumentLiteImpl.getCached(
153             KSNameImpl.getCached(name.asString()),
154             constantValue.toValue(ownerAnnotation),
155             ownerAnnotation
156         )
157     }
158     val presentValueArgumentNames = presentValueArguments.map { it.name.asString() }
159     val argumentsFromDefault = this.type.getDefaultConstructorArguments(presentValueArgumentNames, ownerAnnotation)
160     return presentValueArguments.plus(argumentsFromDefault)
161 }
162 
getDefaultArgumentsnull163 internal fun AnnotationDescriptor.getDefaultArguments(ownerAnnotation: KSAnnotation): List<KSValueArgument> {
164     return this.type.getDefaultConstructorArguments(emptyList(), ownerAnnotation)
165 }
166 
toDeclarationDescriptornull167 internal fun TypeConstructor.toDeclarationDescriptor(): ClassDescriptor? {
168     if (this.declarationDescriptor !is NotFoundClasses.MockClassDescriptor) {
169         return this.declarationDescriptor as? ClassDescriptor
170     }
171     val fqName = (this.declarationDescriptor as? ClassDescriptor)?.fqNameSafe ?: return null
172     val shortNames = fqName.shortName().asString().split("$")
173     var parent = ResolverImpl.instance!!
174         .getClassDeclarationByName("${fqName.parent().asString()}.${shortNames.first()}")
175     for (i in 1 until shortNames.size) {
176         if (parent == null) {
177             return null
178         }
179         parent = parent.declarations
180             .filterIsInstance<KSClassDeclaration>()
181             .singleOrNull { it.simpleName.asString() == shortNames[i] }
182     }
183     return parent?.let { ResolverImpl.instance!!.resolveClassDeclaration(it) }
184 }
185 
getDefaultConstructorArgumentsnull186 internal fun KotlinType.getDefaultConstructorArguments(
187     excludeNames: List<String>,
188     ownerAnnotation: KSAnnotation
189 ): List<KSValueArgument> {
190     return this.constructor.toDeclarationDescriptor()?.constructors?.single()
191         ?.getAbsentDefaultArguments(excludeNames, ownerAnnotation) ?: emptyList()
192 }
193 
ClassConstructorDescriptornull194 fun ClassConstructorDescriptor.getAbsentDefaultArguments(
195     excludeNames: List<String>,
196     ownerAnnotation: KSAnnotation
197 ): List<KSValueArgument> {
198     return this.valueParameters
199         .filterNot { param -> excludeNames.contains(param.name.asString()) || !param.hasDefaultValue() }
200         .map { param ->
201             KSValueArgumentLiteImpl.getCached(
202                 KSNameImpl.getCached(param.name.asString()),
203                 param.getDefaultValue(ownerAnnotation),
204                 ownerAnnotation,
205                 Origin.SYNTHETIC
206             )
207         }
208 }
209 
ValueParameterDescriptornull210 fun ValueParameterDescriptor.getDefaultValue(ownerAnnotation: KSAnnotation): Any? {
211 
212     // Copied from kotlin compiler
213     // TODO: expose in upstream
214     fun convertTypeToKClassValue(javaType: JavaType): KClassValue? {
215         var type = javaType
216         var arrayDimensions = 0
217         while (type is JavaArrayType) {
218             type = type.componentType
219             arrayDimensions++
220         }
221         return when (type) {
222             is JavaPrimitiveType -> {
223                 val primitiveType = type.type
224                     // void.class is not representable in Kotlin, we approximate it by Unit::class
225                     ?: return KClassValue(ClassId.topLevel(StandardNames.FqNames.unit.toSafe()), 0)
226                 if (arrayDimensions > 0) {
227                     KClassValue(ClassId.topLevel(primitiveType.arrayTypeFqName), arrayDimensions - 1)
228                 } else {
229                     KClassValue(ClassId.topLevel(primitiveType.typeFqName), arrayDimensions)
230                 }
231             }
232             is JavaClassifierType -> {
233                 val fqName = FqName(type.classifierQualifiedName)
234                 // TODO: support nested classes somehow
235                 val classId = JavaToKotlinClassMap.mapJavaToKotlin(fqName) ?: ClassId.topLevel(fqName)
236                 KClassValue(classId, arrayDimensions)
237             }
238             else -> null
239         }
240     }
241 
242     // Copied from kotlin compiler
243     // TODO: expose in upstream
244     fun JavaAnnotationArgument.convert(expectedType: KotlinType): ConstantValue<*>? {
245         return when (this) {
246             is JavaLiteralAnnotationArgument -> value?.let {
247                 when (value) {
248                     // Note: `value` expression may be of class that does not match field type in some cases
249                     // tested for Int, left other checks just in case
250                     is Byte, is Short, is Int, is Long -> {
251                         ConstantValueFactory.createIntegerConstantValue((value as Number).toLong(), expectedType, false)
252                     }
253                     else -> {
254                         ConstantValueFactory.createConstantValue(value)
255                     }
256                 }
257             }
258             is JavaEnumValueAnnotationArgument -> {
259                 enumClassId?.let { enumClassId ->
260                     entryName?.let { entryName ->
261                         EnumValue(enumClassId, entryName)
262                     }
263                 }
264             }
265             is JavaArrayAnnotationArgument -> {
266                 val elementType = expectedType.builtIns.getArrayElementType(expectedType)
267                 ConstantValueFactory.createArrayValue(
268                     getElements().mapNotNull { it.convert(elementType) },
269                     expectedType
270                 )
271             }
272             is JavaAnnotationAsAnnotationArgument -> {
273                 AnnotationValue(
274                     LazyJavaAnnotationDescriptor(ResolverImpl.instance!!.lazyJavaResolverContext, this.getAnnotation())
275                 )
276             }
277             is JavaClassObjectAnnotationArgument -> {
278                 convertTypeToKClassValue(getReferencedType())
279             }
280             else -> null
281         }
282     }
283 
284     val psi = this.findPsi()
285     return when (psi) {
286         null -> {
287             val file = if (this.source is JavaSourceElement) {
288                 (
289                     ((this.source as JavaSourceElement).javaElement as? BinaryJavaMethod)
290                         ?.containingClass as? VirtualFileBoundJavaClass
291                     )?.virtualFile?.contentsToByteArray()
292             } else {
293                 (this.containingDeclaration.getContainingKotlinJvmBinaryClass() as? VirtualFileKotlinClass)
294                     ?.file?.contentsToByteArray()
295             }
296             if (file == null) {
297                 null
298             } else {
299                 var defaultValue: JavaAnnotationArgument? = null
300                 ClassReader(file).accept(
301                     object : ClassVisitor(API_VERSION) {
302                         override fun visitMethod(
303                             access: Int,
304                             name: String?,
305                             desc: String?,
306                             signature: String?,
307                             exceptions: Array<out String>?
308                         ): MethodVisitor {
309                             return if (name == this@getDefaultValue.name.asString()) {
310                                 object : MethodVisitor(API_VERSION) {
311                                     override fun visitAnnotationDefault(): AnnotationVisitor =
312                                         BinaryJavaAnnotationVisitor(
313                                             ClassifierResolutionContext { null },
314                                             BinaryClassSignatureParser()
315                                         ) {
316                                             defaultValue = it
317                                         }
318                                 }
319                             } else {
320                                 object : MethodVisitor(API_VERSION) {}
321                             }
322                         }
323                     },
324                     ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES
325                 )
326                 if (!this.type.isError) {
327                     defaultValue?.convert(this.type)?.toValue(ownerAnnotation)
328                 } else {
329                     KSErrorType
330                 }
331             }
332         }
333         is KtParameter -> if (!this.type.isError) {
334             ResolverImpl.instance!!.evaluateConstant(psi.defaultValue, this.type)?.toValue(ownerAnnotation)
335         } else {
336             KSErrorType
337         }
338         is PsiAnnotationMethod -> {
339             when (psi.defaultValue) {
340                 is PsiAnnotation -> KSAnnotationJavaImpl.getCached(psi.defaultValue as PsiAnnotation)
341                 else -> JavaPsiFacade.getInstance(psi.project).constantEvaluationHelper
342                     .computeConstantExpression((psi).defaultValue)
343             }
344         }
345         else -> throw IllegalStateException("Unexpected psi ${psi.javaClass}, $ExceptionMessage")
346     }
347 }
348