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

commonMain.co.touchlab.skie.kir.descriptor.NativeDescriptorProvider.kt Maven / Gradle / Ivy

Go to download

Kotlin compiler plugin that improves Swift interface of a Kotlin Multiplatform framework.

The newest version!
@file:Suppress("invisible_reference", "invisible_member")

package co.touchlab.skie.kir.descriptor

import co.touchlab.skie.compilerinject.reflection.reflectedBy
import co.touchlab.skie.compilerinject.reflection.reflectors.UserVisibleIrModulesSupportReflector
import org.jetbrains.kotlin.backend.common.serialization.findSourceFile
import org.jetbrains.kotlin.backend.konan.KonanConfig
import org.jetbrains.kotlin.backend.konan.ir.konanLibrary
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportMapper
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportedInterface
import org.jetbrains.kotlin.backend.konan.objcexport.getClassIfCategory
import org.jetbrains.kotlin.backend.konan.objcexport.isBaseMethod
import org.jetbrains.kotlin.backend.konan.objcexport.isBaseProperty
import org.jetbrains.kotlin.backend.konan.objcexport.isTopLevel
import org.jetbrains.kotlin.backend.konan.objcexport.shouldBeExposed
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.SourceFile
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.isRecursiveInlineOrValueClassType
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.utils.ResolvedDependency

fun interface ExposedModulesProvider {

    fun exposedModules(): Set
}

internal class NativeDescriptorProvider(
    private val exposedModulesProvider: ExposedModulesProvider,
    private val konanConfig: KonanConfig,
    val objCExportedInterface: ObjCExportedInterface,
) : DescriptorProvider {

    override val builtIns: KotlinBuiltIns by lazy {
        exposedModules.first().builtIns
    }

    override val extraDescriptorBuiltins: ExtraDescriptorBuiltins by lazy {
        ExtraDescriptorBuiltins(exposedModules)
    }

    override val exposedModules: Set by lazy {
        exposedModulesProvider.exposedModules()
    }

    private val mutableExposedClasses by lazy {
        objCExportedInterface.generatedClasses.filterNot { it.defaultType.isRecursiveInlineOrValueClassType() }.toMutableSet()
    }

    override val exposedClasses: Set by ::mutableExposedClasses

    private val mutableTopLevel by lazy {
        objCExportedInterface.topLevel
            .mapValues { it.value.toMutableList() }
            .filter { it.value.isNotEmpty() }
            .toMutableMap()
    }

    override val exposedFiles: Set
        get() = mutableTopLevel.keys.toSet()

    private val mutableExposedCategoryMembers by lazy {
        objCExportedInterface.categoryMembers
            .mapValues { it.value.toMutableList() }
            .filter { it.value.isNotEmpty() }
            .toMutableMap()
    }

    override val exposedCategoryMembers: Set
        get() = mutableExposedCategoryMembers.values.flatten().toSet()

    override val exposedTopLevelMembers: Set
        get() = mutableTopLevel.values.flatten().toSet()

    private val mapper: ObjCExportMapper by lazy {
        objCExportedInterface.mapper
    }

    override val externalDependencies: Set by lazy {
        konanConfig.userVisibleIrModulesSupport
            .reflectedBy()
            .externalDependencyModules
            .toSet()
    }

    override val resolvedLibraries: List by lazy {
        konanConfig.resolvedLibraries.getFullList()
    }

    override val buildInLibraries: Set by lazy {
        resolvedLibraries.filter { it.isDefault }.toSet()
    }

    override val externalLibraries: Set by lazy {
        val externalLibrariesArtifacts = externalDependencies.flatMap { it.artifactPaths }.map { it.path }.toSet()

        resolvedLibraries
            .filter { it.libraryFile.absolutePath in externalLibrariesArtifacts }
            .toSet() - buildInLibraries
    }

    override val localLibraries: Set by lazy {
        resolvedLibraries.toSet() - buildInLibraries - externalLibraries
    }

    override fun isFromLocalModule(declarationDescriptor: DeclarationDescriptor): Boolean =
        declarationDescriptor.module.konanLibrary in localLibraries

    override fun isExposed(callableMemberDescriptor: CallableMemberDescriptor): Boolean =
        callableMemberDescriptor.isExposed

    override fun isExposable(callableMemberDescriptor: CallableMemberDescriptor): Boolean =
        callableMemberDescriptor.isExposable

    override fun isExposable(classDescriptor: ClassDescriptor): Boolean =
        classDescriptor.isExposable

    override fun isBaseMethod(functionDescriptor: FunctionDescriptor): Boolean =
        mapper.isBaseMethod(functionDescriptor)

    override fun isBaseProperty(propertyDescriptor: PropertyDescriptor): Boolean =
        mapper.isBaseProperty(propertyDescriptor)

    @get:JvmName("isExposableExtension")
    private val CallableMemberDescriptor.isExposable: Boolean
        get() = mapper.shouldBeExposed(this)

    @get:JvmName("isExposableExtension")
    private val ClassDescriptor.isExposable: Boolean
        get() = mapper.shouldBeExposed(this)

    @get:JvmName("isExposedExtension")
    private val CallableMemberDescriptor.isExposed: Boolean
        get() = this in exposedTopLevelMembers ||
            this in exposedCategoryMembers ||
            (this.containingDeclaration in exposedClasses && this.isExposable)

    fun registerExposedDescriptor(descriptor: DeclarationDescriptor) {
        when (descriptor) {
            is ClassDescriptor -> registerDescriptor(descriptor)
            is CallableMemberDescriptor -> registerDescriptor(descriptor)
            else -> error("Unsupported descriptor type: $descriptor.")
        }
    }

    private fun registerDescriptor(descriptor: ClassDescriptor) {
        if (!descriptor.isExposable) {
            return
        }

        mutableExposedClasses.add(descriptor)
    }

    private fun registerDescriptor(descriptor: CallableMemberDescriptor) {
        if (!descriptor.isExposable) {
            return
        }

        if (descriptor.containingDeclaration is ClassDescriptor) {
            return
        }

        if (mapper.isTopLevel(descriptor)) {
            val descriptors = mutableTopLevel.getOrPut(descriptor.findSourceFile()) {
                mutableListOf()
            }

            descriptors.add(descriptor)
        } else {
            val categoryClass = mapper.getClassIfCategory(descriptor) ?: error("$descriptor is neither top level nor category.")
            val descriptors = mutableExposedCategoryMembers.getOrPut(categoryClass) {
                mutableListOf()
            }

            descriptors.add(descriptor)
        }
    }

    override fun getFileModule(file: SourceFile): ModuleDescriptor =
        mutableTopLevel[file]?.firstOrNull()?.module ?: error("File $file is not known to contain exported top level declarations.")

    override fun getExposedClassMembers(classDescriptor: ClassDescriptor): List =
        classDescriptor.unsubstitutedMemberScope
            .getDescriptorsFiltered()
            .filterIsInstance()
            .filter { it.isExposed }

    override fun getExposedCategoryMembers(classDescriptor: ClassDescriptor): List =
        mutableExposedCategoryMembers[classDescriptor] ?: emptyList()

    override fun getExposedConstructors(classDescriptor: ClassDescriptor): List =
        classDescriptor.constructors.filter { it.isExposed }

    override fun getExposedStaticMembers(file: SourceFile): List =
        mutableTopLevel[file] ?: emptyList()

    override fun getReceiverClassDescriptorOrNull(descriptor: CallableMemberDescriptor): ClassDescriptor? {
        val categoryClass = mapper.getClassIfCategory(descriptor)
        val containingDeclaration = descriptor.containingDeclaration

        return when {
            categoryClass != null -> categoryClass
            descriptor is PropertyAccessorDescriptor -> getReceiverClassDescriptorOrNull(descriptor.correspondingProperty)
            containingDeclaration is ClassDescriptor -> containingDeclaration
            else -> null
        }
    }

    override fun getExposedCompanionObject(classDescriptor: ClassDescriptor): ClassDescriptor? =
        classDescriptor.companionObjectDescriptor?.takeIf { it.isExposable }

    override fun getExposedNestedClasses(classDescriptor: ClassDescriptor): List =
        classDescriptor.unsubstitutedInnerClassesScope
            .getDescriptorsFiltered(DescriptorKindFilter.CLASSIFIERS)
            .filterIsInstance()
            .filter { it.kind == ClassKind.CLASS || it.kind == ClassKind.OBJECT || it.kind == ClassKind.ENUM_CLASS }
            .filter { it.isExposable }

    override fun getExposedEnumEntries(classDescriptor: ClassDescriptor): List =
        classDescriptor.unsubstitutedInnerClassesScope
            .getDescriptorsFiltered(DescriptorKindFilter.CLASSIFIERS)
            .filterIsInstance()
            .filter { it.kind == ClassKind.ENUM_ENTRY }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy