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

org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2020 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.jvm.lower.inlineclasses

import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.codegen.state.InfoForMangling
import org.jetbrains.kotlin.codegen.state.collectFunctionSignatureForManglingSuffix
import org.jetbrains.kotlin.codegen.state.md5base64
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.name.Name

/**
 * Replace inline classes by their underlying types.
 */
fun IrType.unboxInlineClass() = InlineClassAbi.unboxType(this) ?: this

object InlineClassAbi {
    /**
     * An origin for IrFunctionReferences which prevents inline class mangling. This only exists because of
     * inconsistencies between `RuntimeTypeMapper` and `KotlinTypeMapper`. The `RuntimeTypeMapper` does not
     * perform inline class mangling and so in the absence of jvm signatures in the metadata we need to avoid
     * inline class mangling as well in the function references used as arguments to the signature string intrinsic.
     */
    object UNMANGLED_FUNCTION_REFERENCE : IrStatementOriginImpl("UNMANGLED_FUNCTION_REFERENCE")

    /**
     * Unwraps inline class types to their underlying representation.
     * Returns null if the type cannot be unboxed.
     */
    internal fun unboxType(type: IrType): IrType? {
        val klass = type.classOrNull?.owner ?: return null
        if (!klass.isInline) return null

        // TODO: Apply type substitutions
        val underlyingType = getUnderlyingType(klass).unboxInlineClass()
        if (!type.isNullable())
            return underlyingType
        if (underlyingType.isNullable() || underlyingType.isPrimitiveType())
            return null
        return underlyingType.makeNullable()
    }

    /**
     * Get the underlying type of an inline class based on the single argument to its
     * primary constructor. This is what the current jvm backend does.
     *
     * Looking for a backing field does not work for unsigned types, which don't
     * contain a field.
     */
    fun getUnderlyingType(irClass: IrClass): IrType {
        require(irClass.isInline)
        return irClass.primaryConstructor!!.valueParameters[0].type
    }

    /**
     * Returns a mangled name for a function taking inline class arguments
     * to avoid clashes between overloaded methods.
     */
    fun mangledNameFor(irFunction: IrFunction, mangleReturnTypes: Boolean, useOldMangleRules: Boolean): Name {
        if (irFunction is IrConstructor) {
            // Note that we might drop this convention and use standard mangling for constructors too, see KT-37186.
            assert(irFunction.constructedClass.isInline) {
                "Should not mangle names of non-inline class constructors: ${irFunction.render()}"
            }
            return Name.identifier("constructor-impl")
        }

        val suffix = hashSuffix(irFunction, mangleReturnTypes, useOldMangleRules) ?: return irFunction.name

        val base = when {
            irFunction.isGetter ->
                JvmAbi.getterName(irFunction.propertyName.asString())
            irFunction.isSetter ->
                JvmAbi.setterName(irFunction.propertyName.asString())
            irFunction.name.isSpecial ->
                error("Unhandled special name in mangledNameFor: ${irFunction.name}")
            else ->
                irFunction.name.asString()
        }

        return Name.identifier("$base-$suffix")
    }

    private fun hashSuffix(
        irFunction: IrFunction,
        mangleReturnTypes: Boolean,
        useOldMangleRules: Boolean,
        alwaysMangleReturnType: Boolean = false
    ): String? {
        val signatureForMangling = collectFunctionSignatureForManglingSuffix(
            useOldManglingRules = useOldMangleRules,
            requiresFunctionNameManglingForParameterTypes = irFunction.fullValueParameterList.any { it.type.requiresMangling },
            fqNamesForMangling = irFunction.fullValueParameterList.map {
                it.type.asInfoForMangling()
            } + listOfNotNull(
                // The JVM backend computes mangled names after creating suspend function views, but before default argument
                // stub insertion. It would be nice if this part of the continuation lowering happened earlier in the pipeline.
                // TODO: Move suspend function view creation before JvmInlineClassLowering.
                if (irFunction.isSuspend)
                    InfoForMangling(FqNameUnsafe("kotlin.coroutines.Continuation"), isInline = false, isNullable = false)
                else null
            ),
            returnTypeInfo = if (alwaysMangleReturnType || (mangleReturnTypes && irFunction.hasMangledReturnType)) {
                irFunction.returnType.asInfoForMangling()
            } else null
        )

        return when {
            signatureForMangling != null -> md5base64(signatureForMangling)
            (irFunction.parent as? IrClass)?.isInline == true && irFunction.origin != IrDeclarationOrigin.IR_BUILTINS_STUB -> "impl"
            else -> null
        }
    }

    fun hashReturnSuffix(irFunction: IrFunction): String =
        hashSuffix(
            irFunction,
            mangleReturnTypes = true,
            useOldMangleRules = false,
            alwaysMangleReturnType = true
        )!!

    private fun IrType.asInfoForMangling(): InfoForMangling =
        InfoForMangling(
            erasedUpperBound.fqNameWhenAvailable!!.toUnsafe(),
            isInline = getClass()?.isInline == true,
            isNullable = isNullable()
        )

    private val IrFunction.propertyName: Name
        get() = (this as IrSimpleFunction).correspondingPropertySymbol!!.owner.name
}

internal val IrType.requiresMangling: Boolean
    get() {
        val irClass = erasedUpperBound
        return irClass.isInline && irClass.fqNameWhenAvailable != StandardNames.RESULT_FQ_NAME
    }

internal val IrFunction.fullValueParameterList: List
    get() = listOfNotNull(extensionReceiverParameter) + valueParameters

internal val IrFunction.hasMangledParameters: Boolean
    get() = dispatchReceiverParameter != null && parentAsClass.isInline ||
            fullValueParameterList.any { it.type.requiresMangling } ||
            (this is IrConstructor && constructedClass.isInline)

internal val IrFunction.hasMangledReturnType: Boolean
    get() = returnType.erasedUpperBound.isInline && parentClassOrNull?.isFileClass != true

internal val IrClass.inlineClassFieldName: Name
    get() = primaryConstructor!!.valueParameters.single().name

val IrFunction.isInlineClassFieldGetter: Boolean
    get() = (parent as? IrClass)?.isInline == true && this is IrSimpleFunction && extensionReceiverParameter == null &&
            correspondingPropertySymbol?.let { it.owner.getter == this && it.owner.name == parentAsClass.inlineClassFieldName } == true

val IrFunction.isPrimaryInlineClassConstructor: Boolean
    get() = this is IrConstructor && isPrimary && constructedClass.isInline




© 2015 - 2024 Weber Informatics LLC | Privacy Policy