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

org.jetbrains.kotlin.backend.common.serialization.KotlinMangler.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC2
Show newest version
/*
 * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.backend.common.serialization

import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name

interface KotlinMangler {
    val String.hashMangle: Long
    val IrDeclaration.hashedMangle: Long
    fun IrDeclaration.isExported(): Boolean
    val IrFunction.functionName: String
    val IrType.isInlined: Boolean
    val Long.isSpecial: Boolean

    companion object {
        private val FUNCTION_PREFIX = ""
        fun functionClassSymbolName(name: Name) = "ktype:$FUNCTION_PREFIX$name"
        fun functionInvokeSymbolName(name: Name) = "kfun:$FUNCTION_PREFIX$name.invoke"
    }
}

abstract class KotlinManglerImpl : KotlinMangler {
    override val String.hashMangle get() = this.cityHash64()

    private fun hashedMangleImpl(declaration: IrDeclaration): Long {
        return declaration.uniqSymbolName().hashMangle
    }

    override val IrDeclaration.hashedMangle: Long
        get() = hashedMangleImpl(this)


    // We can't call "with (super) { this.isExported() }" in children.
    // So provide a hook.
    protected open fun IrDeclaration.isPlatformSpecificExported(): Boolean = false

    override fun IrDeclaration.isExported(): Boolean = isExportedImpl(this)

    /**
     * Defines whether the declaration is exported, i.e. visible from other modules.
     *
     * Exported declarations must have predictable and stable ABI
     * that doesn't depend on any internal transformations (e.g. IR lowering),
     * and so should be computable from the descriptor itself without checking a backend state.
     */
    private tailrec fun isExportedImpl(declaration: IrDeclaration): Boolean {
        // TODO: revise
        val descriptorAnnotations = declaration.descriptor.annotations

        if (declaration.isPlatformSpecificExported()) return true

        if (declaration is IrTypeAlias && declaration.parent is IrPackageFragment) {
            return true
        }

        if (descriptorAnnotations.hasAnnotation(publishedApiAnnotation)) {
            return true
        }

        if (declaration.isAnonymousObject)
            return false

        if (declaration is IrConstructor && declaration.constructedClass.kind.isSingleton) {
            // Currently code generator can access the constructor of the singleton,
            // so ignore visibility of the constructor itself.
            return isExportedImpl(declaration.constructedClass)
        }

        if (declaration is IrFunction) {
            val descriptor = declaration.descriptor
            // TODO: this code is required because accessor doesn't have a reference to property.
            if (descriptor is PropertyAccessorDescriptor) {
                val property = descriptor.correspondingProperty
                if (property.annotations.hasAnnotation(publishedApiAnnotation)) return true
            }
        }

        val visibility = when (declaration) {
            is IrClass -> declaration.visibility
            is IrFunction -> declaration.visibility
            is IrProperty -> declaration.visibility
            is IrField -> declaration.visibility
            is IrTypeAlias -> declaration.visibility
            else -> null
        }

        /**
         * note: about INTERNAL - with support of friend modules we let frontend to deal with internal declarations.
         */
        if (visibility != null && !visibility.isPublicAPI && visibility != Visibilities.INTERNAL) {
            // If the declaration is explicitly marked as non-public,
            // then it must not be accessible from other modules.
            return false
        }

        val parent = declaration.parent
        if (parent is IrDeclaration) {
            return isExportedImpl(parent)
        }

        return true
    }

    private val publishedApiAnnotation = FqName("kotlin.PublishedApi")

    protected fun acyclicTypeMangler(visited: MutableSet, type: IrType): String {
        val descriptor = (type.classifierOrNull as? IrTypeParameterSymbol)?.owner
        if (descriptor != null) {
            val upperBounds = if (visited.contains(descriptor)) "" else {

                visited.add(descriptor)

                descriptor.superTypes.map {
                    val bound = acyclicTypeMangler(visited, it)
                    if (bound == "kotlin.Any?") "" else "_$bound"
                }.joinToString("")
            }
            return "#GENERIC${if (type.isMarkedNullable()) "?" else ""}$upperBounds"
        }

        var hashString = type.getClass()?.run { fqNameForIrSerialization.asString() } ?: ""

        when (type) {
            is IrSimpleType -> {
                if (!type.arguments.isEmpty()) {
                    hashString += "<${type.arguments.map {
                        when (it) {
                            is IrStarProjection -> "#STAR"
                            is IrTypeProjection -> {
                                val variance = it.variance.label
                                val projection = if (variance == "") "" else "${variance}_"
                                projection + acyclicTypeMangler(visited, it.type)
                            }
                            else -> error(it)
                        }
                    }.joinToString(",")}>"
                }

                if (type.hasQuestionMark) hashString += "?"
            }
            !is IrDynamicType -> {
                error(type)
            }
        }
        return hashString
    }

    protected fun typeToHashString(type: IrType) = acyclicTypeMangler(mutableSetOf(), type)

    val IrValueParameter.extensionReceiverNamePart: String
        get() = "@${typeToHashString(this.type)}."

    open val IrFunction.argsPart
        get() = this.valueParameters.map {
            "${typeToHashString(it.type)}${if (it.isVararg) "_VarArg" else ""}"
        }.joinToString(";")

    open val IrFunction.signature: String
        get() {
            val extensionReceiverPart = this.extensionReceiverParameter?.extensionReceiverNamePart ?: ""
            val argsPart = this.argsPart
            // Distinguish value types and references - it's needed for calling virtual methods through bridges.
            // Also is function has type arguments - frontend allows exactly matching overrides.
            val signatureSuffix =
                when {
                    this.typeParameters.isNotEmpty() -> "Generic"
                    returnType.isInlined -> "ValueType"
                    !returnType.isUnitOrNullableUnit() -> typeToHashString(returnType)
                    else -> ""
                }
            return "$extensionReceiverPart($argsPart)$signatureSuffix"
        }

    open val IrFunction.platformSpecificFunctionName: String? get() = null

    // TODO: rename to indicate that it has signature included
    override val IrFunction.functionName: String
        get() {
            // TODO: Again. We can't call super in children, so provide a hook for now.
            this.platformSpecificFunctionName?.let { return it }
            val name = this.name.mangleIfInternal(this.module, this.visibility)
            return "$name$signature"
        }

    override val Long.isSpecial: Boolean
        get() = specialHashes.contains(this)

    fun Name.mangleIfInternal(moduleDescriptor: ModuleDescriptor, visibility: Visibility): String =
        if (visibility != Visibilities.INTERNAL) {
            this.asString()
        } else {
            val moduleName = moduleDescriptor.name.asString()
                .let { it.substring(1, it.lastIndex) } // Remove < and >.

            "$this\$$moduleName"
        }

    val IrField.symbolName: String
        get() {
            val containingDeclarationPart = parent.fqNameForIrSerialization.let {
                if (it.isRoot) "" else "$it."
            }
            return "kfield:$containingDeclarationPart$name"

        }

    val IrClass.typeInfoSymbolName: String
        get() {
            assert(isExportedImpl(this))
            if (isBuiltInFunction(this))
                return KotlinMangler.functionClassSymbolName(name)
            return "ktype:" + this.fqNameForIrSerialization.toString()
        }

    val IrTypeParameter.symbolName: String
        get() {

            val parentDeclaration = (parent as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: parent
            val containingDeclarationPart = when (parentDeclaration) {
                is IrDeclaration -> parentDeclaration.uniqSymbolName()
                else -> error("Unexpected type parameter parent")
            }
            return "ktypeparam:$containingDeclarationPart$name@$index"
        }

    val IrTypeAlias.symbolName: String
        get() {
            val containingDeclarationPart = parent.fqNameForIrSerialization.let {
                if (it.isRoot) "" else "$it."
            }
            return "ktypealias:$containingDeclarationPart$name"
        }

// This is a little extension over what's used in real mangling
// since some declarations never appear in the bitcode symbols.

    internal fun IrDeclaration.uniqSymbolName(): String = when (this) {
        is IrFunction -> this.uniqFunctionName
        is IrProperty -> this.symbolName
        is IrClass -> this.typeInfoSymbolName
        is IrField -> this.symbolName
        is IrEnumEntry -> this.symbolName
        is IrTypeParameter -> this.symbolName
        is IrTypeAlias -> this.symbolName
        else -> error("Unexpected exported declaration: $this")
    }

    private val IrDeclarationParent.fqNameUnique: FqName
        get() = when (this) {
            is IrPackageFragment -> this.fqName
            is IrDeclaration -> this.parent.fqNameUnique.child(this.uniqName)
            else -> error(this)
        }

    private val IrDeclaration.uniqName: Name
        get() = when (this) {
            is IrSimpleFunction -> Name.special("<${this.uniqFunctionName}>")
            else -> this.nameForIrSerialization
        }

    private val IrProperty.symbolName: String
        get() {
            val extensionReceiver: String = getter!!.extensionReceiverParameter?.extensionReceiverNamePart ?: ""

            val containingDeclarationPart = parent.fqNameForIrSerialization.let {
                if (it.isRoot) "" else "$it."
            }
            return "kprop:$containingDeclarationPart$extensionReceiver$name"
        }

    private val IrEnumEntry.symbolName: String
        get() {
            val containingDeclarationPart = parent.fqNameForIrSerialization.let {
                if (it.isRoot) "" else "$it."
            }
            return "kenumentry:$containingDeclarationPart$name"
        }

    // This is basicly the same as .symbolName, but disambiguates external functions with the same C name.
// In addition functions appearing in fq sequence appear as .
    private val IrFunction.uniqFunctionName: String
        get() {
            if (isBuiltInFunction(this))
                return KotlinMangler.functionInvokeSymbolName(parentAsClass.name)
            val parent = this.parent

            val containingDeclarationPart = parent.fqNameUnique.let {
                if (it.isRoot) "" else "$it."
            }

            return "kfun:$containingDeclarationPart#$functionName"
        }

    private val specialHashes = listOf("Function", "KFunction", "SuspendFunction", "KSuspendFunction")
        .flatMap { name ->
            (0..255).map { KotlinMangler.functionClassSymbolName(Name.identifier(name + it)) }
        }.map { it.hashMangle }
        .toSet()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy