All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jetbrains.plugin.structure.classes.utils.KtClassResolver.kt Maven / Gradle / Ivy

package com.jetbrains.plugin.structure.classes.utils

import kotlinx.metadata.jvm.KotlinClassMetadata
import kotlinx.metadata.jvm.Metadata
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
import org.slf4j.Logger
import org.slf4j.LoggerFactory

internal const val KOTLIN_METADATA_ANNOTATION_DESC = "Lkotlin/Metadata;"

private typealias Signature = String

private val LOG: Logger = LoggerFactory.getLogger(KtClassResolver::class.java)

class KtClassResolver {
  private val cache = HashMap()

  operator fun get(classNode: ClassNode): KtClassNode? {
    val signature: Signature = classNode.signature ?: return classNode.ktClassNode
    return if (signature in cache) {
      cache[signature]
    } else {
      classNode.ktClassNode?.also {
        cache[signature] = it
      }
    }
  }

  private val ClassNode.ktClassNode: KtClassNode?
    get() = findMetadataAnnotation(this)
      ?.let { annotation -> getKtClassNode(this, annotation) }

  private fun getKtClassNode(classNode: ClassNode, metadataAnnotation: Metadata): KtClassNode? {
    return readMetadata(classNode, metadataAnnotation)?.let { metadata ->
      KtClassNode(classNode, metadata)
    }
  }

  private fun findMetadataAnnotation(classNode: ClassNode): Metadata? {
    return classNode.allAnnotations
      .firstOrNull { it.desc == KOTLIN_METADATA_ANNOTATION_DESC }
      ?.toMetadata()
  }

  private fun readMetadata(classNode: ClassNode, metadataAnnotation: Metadata): KotlinClassMetadata.Class? {
    return try {
      if (metadataAnnotation.metadataVersion.isNotEmpty()) {
        KotlinClassMetadata.readStrict(metadataAnnotation) as? KotlinClassMetadata.Class
      } else {
        LOG.debug("Class [${classNode.name}] has @kotlin.Metadata annotation without 'metadataVersion' attribute. It cannot be read")
        null
      }
    } catch (e: IllegalArgumentException) {
      LOG.debug("Failed to read Kotlin metadata for class ${classNode.name}", e)
      null
    }
  }

  private fun AnnotationNode.toMetadata() = Metadata(
    kind = int("k"),
    metadataVersion = ints("mv"),
    data1 = strings("d1"),
    data2 = strings("d2"))

  private fun AnnotationNode.int(key: String) = get(key)
  private fun AnnotationNode.ints(key: String) = get?>(key)?.toIntArray()
  private fun AnnotationNode.strings(key: String) = get?>(key)?.toTypedArray()

  private inline fun  AnnotationNode.get(key: String): T? {
    return getAnnotationValue(key)?.let { value ->
      if (value is T) value else null
    }
  }

  private fun AnnotationNode.getAnnotationValue(key: String): Any? {
    val values = values ?: return null
    for (i in 0 until values.size step 2) {
      if (values[i] == key) {
        return values[i + 1]
      }
    }
    return null
  }

  companion object {
    fun hasKotlinMetadataAnnotation(classNode: ClassNode): Boolean {
      return classNode.allAnnotations.any { it.desc == KOTLIN_METADATA_ANNOTATION_DESC }
    }
  }
}

private val ClassNode.allAnnotations: List
  get() = (visibleAnnotations.orEmpty() + invisibleAnnotations.orEmpty())




© 2015 - 2025 Weber Informatics LLC | Privacy Policy