| /* |
| * Copyright 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.room.compiler.processing.javac.kotlin |
| |
| import kotlinx.metadata.jvm.KotlinClassHeader |
| import kotlinx.metadata.jvm.KotlinClassMetadata |
| import javax.lang.model.element.Element |
| import javax.lang.model.element.ElementKind |
| import javax.lang.model.element.ExecutableElement |
| |
| /** |
| * Utility class for processors that wants to run kotlin specific code. |
| */ |
| internal class KotlinMetadataElement( |
| val element: Element, |
| private val classMetadata: KotlinClassMetadata.Class |
| ) { |
| private val typeInfo: KmClassTypeInfo by lazy { |
| lateinit var result: KmClassTypeInfo |
| classMetadata.accept( |
| ClassAsKmTypeReader { |
| result = it |
| } |
| ) |
| result |
| } |
| val kmType |
| get() = typeInfo.kmType |
| val superType |
| get() = typeInfo.superType |
| private val functionList: List<KmFunction> by lazy { classMetadata.readFunctions() } |
| private val constructorList: List<KmConstructor> by lazy { classMetadata.readConstructors() } |
| private val propertyList: List<KmProperty> by lazy { classMetadata.readProperties() } |
| private val classFlags: KotlinMetadataClassFlags by lazy { |
| KotlinMetadataClassFlags(classMetadata) |
| } |
| |
| private val ExecutableElement.descriptor: String |
| get() = descriptor() |
| |
| fun findPrimaryConstructorSignature() = constructorList.firstOrNull { |
| it.isPrimary() |
| }?.descriptor |
| |
| fun isObject(): Boolean = classFlags.isObject() |
| fun isCompanionObject(): Boolean = classFlags.isCompanionObject() |
| fun isAnnotationClass(): Boolean = classFlags.isAnnotationClass() |
| fun isClass(): Boolean = classFlags.isClass() |
| fun isInterface(): Boolean = classFlags.isInterface() |
| fun isDataClass(): Boolean = classFlags.isDataClass() |
| fun isValueClass(): Boolean = classFlags.isValueClass() |
| fun isFunctionalInterface(): Boolean = classFlags.isFunctionalInterface() |
| fun isExpect(): Boolean = classFlags.isExpect() |
| |
| fun getFunctionMetadata(method: ExecutableElement): KmFunction? { |
| check(method.kind == ElementKind.METHOD) { |
| "must pass an element type of method" |
| } |
| val methodSignature = method.descriptor |
| return functionList.firstOrNull { it.descriptor == methodSignature } |
| } |
| |
| fun getConstructorMetadata(method: ExecutableElement): KmConstructor? { |
| check(method.kind == ElementKind.CONSTRUCTOR) { |
| "must pass an element type of constructor" |
| } |
| val methodSignature = method.descriptor |
| return constructorList.firstOrNull { it.descriptor == methodSignature } |
| } |
| |
| fun getPropertyMetadata(propertyName: String) = propertyList.firstOrNull { |
| it.name == propertyName |
| } |
| |
| companion object { |
| /** |
| * Creates a [KotlinMetadataElement] for the given element if it contains Kotlin metadata, |
| * otherwise this method returns null. |
| * |
| * Usually the [element] passed must represent a class. For example, if kotlin metadata is |
| * desired for a method, then the containing class should be used as parameter. |
| */ |
| fun createFor(element: Element): KotlinMetadataElement? { |
| val metadata = getMetadataAnnotation(element)?.run { |
| KotlinClassHeader( |
| kind = kind, |
| metadataVersion = metadataVersion, |
| bytecodeVersion = bytecodeVersion, |
| data1 = data1, |
| data2 = data2, |
| extraString = extraString, |
| packageName = packageName, |
| extraInt = extraInt |
| ).let { |
| // TODO: Support more metadata kind (file facade, synthetic class, etc...) |
| KotlinClassMetadata.read(it) as? KotlinClassMetadata.Class |
| } |
| } |
| return if (metadata != null) { |
| KotlinMetadataElement(element, metadata) |
| } else { |
| null |
| } |
| } |
| |
| /** |
| * Search for Kotlin's Metadata annotation across the element's hierarchy. |
| */ |
| private fun getMetadataAnnotation(element: Element?): Metadata? = |
| if (element != null) { |
| element.getAnnotation(Metadata::class.java) |
| ?: getMetadataAnnotation(element.enclosingElement) |
| } else { |
| null |
| } |
| } |
| } |