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