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

org.jetbrains.kotlin.backend.wasm.lower.BuiltInsLowering.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
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.wasm.lower

import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.config.AnalysisFlags
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.lower.calls.EnumIntrinsicsUtils
import org.jetbrains.kotlin.ir.backend.js.utils.erasedUpperBound
import org.jetbrains.kotlin.ir.backend.js.utils.isEqualsInheritedFromAny
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.putClassTypeArgument
import org.jetbrains.kotlin.ir.util.toIrConst
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.parentOrNull

class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
    private val irBuiltins = context.irBuiltIns
    private val symbols = context.wasmSymbols

    private fun IrType.findEqualsMethod(): IrSimpleFunction {
        val klass = getClass() ?: irBuiltins.anyClass.owner
        return klass.functions.single { it.isEqualsInheritedFromAny() }
    }

    private fun transformCall(
        call: IrCall,
        builder: DeclarationIrBuilder
    ): IrExpression {
        when (val symbol = call.symbol) {
            irBuiltins.linkageErrorSymbol -> {
                return irCall(call, context.wasmSymbols.throwLinkageError)
            }
            irBuiltins.ieee754equalsFunByOperandType[irBuiltins.floatClass] -> {
                if (call.getValueArgument(0)!!.type.isNullable() || call.getValueArgument(1)!!.type.isNullable()) {
                    return irCall(call, symbols.nullableFloatIeee754Equals)
                }
                return irCall(call, symbols.floatEqualityFunctions.getValue(irBuiltins.floatType))
            }
            irBuiltins.ieee754equalsFunByOperandType[irBuiltins.doubleClass] -> {
                if (call.getValueArgument(0)!!.type.isNullable() || call.getValueArgument(1)!!.type.isNullable()) {
                    return irCall(call, symbols.nullableDoubleIeee754Equals)
                }
                return irCall(call, symbols.floatEqualityFunctions.getValue(irBuiltins.doubleType))
            }
            irBuiltins.eqeqSymbol,
            irBuiltins.eqeqeqSymbol -> {
                fun callRefIsNull(expr: IrExpression): IrCall {
                    if (
                        !context.isWasmJsTarget &&
                        expr.type.erasedUpperBound?.isExternal == true
                    ) {
                        error("Unexpected external refs in wasi mode")
                    }
                    val refIsNull = if (expr.type.erasedUpperBound?.isExternal == true) symbols.jsRelatedSymbols.externRefIsNull else symbols.refIsNull
                    return builder.irCall(refIsNull).apply { putValueArgument(0, expr) }
                }

                val lhs = call.getValueArgument(0)!!
                val rhs = call.getValueArgument(1)!!

                if (lhs.isNullConst()) return callRefIsNull(rhs)

                if (rhs.isNullConst()) return callRefIsNull(lhs)

                val lhsType = lhs.type
                val rhsType = rhs.type

                if (lhsType == rhsType) {
                    val newSymbol =
                        symbols.equalityFunctions[lhsType]
                            // For eqeqeqSymbol try to use more efficient comparison if type is Double or Float.
                            // But for eqeqSymbol we have to use generic comparison for floating point numbers.
                            ?: if (call.symbol === irBuiltins.eqeqeqSymbol) symbols.floatEqualityFunctions[lhsType] else null

                    if (newSymbol != null) {
                        return irCall(call, newSymbol)
                    }
                }

                // For eqeqSymbol use overridden `Any.equals(Any?)` if there is any.
                if (call.symbol === irBuiltins.eqeqSymbol && !lhsType.isNullable() && !lhsType.isNothing()) {
                    return irCall(call, lhsType.findEqualsMethod().symbol, argumentsAsReceivers = true)
                }

                val fallbackEqFun = if (call.symbol === irBuiltins.eqeqeqSymbol) symbols.refEq else symbols.nullableEquals
                return irCall(call, fallbackEqFun)
            }

            irBuiltins.checkNotNullSymbol -> {
                val arg = call.getValueArgument(0)!!

                if (arg.isNullConst()) {
                    return builder.irCall(symbols.throwNullPointerException)
                }

                return builder.irComposite {
                    val temporary = irTemporary(arg)
                    +builder.irIfNull(
                        type = arg.type.makeNotNull(),
                        subject = irGet(temporary),
                        thenPart = builder.irCall(symbols.throwNullPointerException),
                        elsePart = irGet(temporary)
                    )
                }
            }
            in symbols.comparisonBuiltInsToWasmIntrinsics.keys -> {
                val newSymbol = symbols.comparisonBuiltInsToWasmIntrinsics[symbol]!!
                return irCall(call, newSymbol)
            }

            irBuiltins.noWhenBranchMatchedExceptionSymbol ->
                return builder.irCall(symbols.throwNoBranchMatchedException, irBuiltins.nothingType)

            irBuiltins.illegalArgumentExceptionSymbol ->
                return builder.irCall(symbols.throwIAE, irBuiltins.nothingType, 1).apply {
                    putValueArgument(0, call.getValueArgument(0)!!)
                }

            irBuiltins.dataClassArrayMemberHashCodeSymbol, irBuiltins.dataClassArrayMemberToStringSymbol -> {
                val argument = call.getValueArgument(0)!!
                val argumentType = argument.type
                val overloadSymbol: IrSimpleFunctionSymbol
                val returnType: IrType
                if (symbol == irBuiltins.dataClassArrayMemberHashCodeSymbol) {
                    overloadSymbol = symbols.findContentHashCodeOverload(argumentType)
                    returnType = irBuiltins.intType
                } else {
                    overloadSymbol = symbols.findContentToStringOverload(argumentType)
                    returnType = irBuiltins.stringType
                }

                return builder.irCall(
                    overloadSymbol,
                    returnType,
                ).apply {
                    extensionReceiver = argument
                    if (argumentType.classOrNull == irBuiltins.arrayClass) {
                        putTypeArgument(0, argumentType.getArrayElementType(irBuiltins))
                    }
                }
            }
            in symbols.startCoroutineUninterceptedOrReturnIntrinsics -> {
                val arity = symbols.startCoroutineUninterceptedOrReturnIntrinsics.indexOf(symbol)
                val newSymbol = irBuiltins.suspendFunctionN(arity).getSimpleFunction("invoke")!!
                return irCall(call, newSymbol, argumentsAsReceivers = true)
            }
            context.reflectionSymbols.getKClass -> {
                val type = call.getTypeArgument(0)!!
                val klass = type.classOrNull?.owner ?: error("Invalid type")

                val constructorArgument: IrExpression
                val kclassConstructor: IrConstructor
                if (klass.isEffectivelyExternal()) {
                    check(context.isWasmJsTarget) { "External classes reflection in WASI mode are not supported" }
                    kclassConstructor = symbols.jsRelatedSymbols.kExternalClassImpl.owner.constructors.first()
                    constructorArgument = getExternalKClassCtorArgument(type, builder)
                } else {
                    kclassConstructor = symbols.reflectionSymbols.kClassImpl.owner.constructors.first()
                    constructorArgument = getKClassCtorArgument(type, builder)
                }

                return IrConstructorCallImpl(
                    startOffset = UNDEFINED_OFFSET,
                    endOffset = UNDEFINED_OFFSET,
                    type = kclassConstructor.returnType,
                    symbol = kclassConstructor.symbol,
                    typeArgumentsCount = 1,
                    valueArgumentsCount = 1,
                    constructorTypeArgumentsCount = 0
                ).also {
                    it.putClassTypeArgument(0, type)
                    it.putValueArgument(0, constructorArgument)
                }
            }

            symbols.enumValueOfIntrinsic ->
                return EnumIntrinsicsUtils.transformEnumValueOfIntrinsic(call)
            symbols.enumValuesIntrinsic ->
                return EnumIntrinsicsUtils.transformEnumValuesIntrinsic(call)
            symbols.enumEntriesIntrinsic ->
                return EnumIntrinsicsUtils.transformEnumEntriesIntrinsic(call)
        }

        return call
    }

    private fun getKClassCtorArgument(type: IrType, builder: DeclarationIrBuilder): IrExpression {
        val klass = type.classOrNull?.owner ?: error("Invalid type")

        val typeId = builder.irCall(symbols.wasmTypeId).also {
            it.putTypeArgument(0, type)
        }

        if (!klass.isInterface) {
            return builder.irCall(context.wasmSymbols.reflectionSymbols.getTypeInfoTypeDataByPtr).also {
                it.putValueArgument(0, typeId)
            }
        } else {
            val fqName = type.classFqName!!
            val fqnShouldBeEmitted =
                context.configuration.languageVersionSettings.getFlag(AnalysisFlags.allowFullyQualifiedNameInKClass)
            val packageName = if (fqnShouldBeEmitted) fqName.parentOrNull()?.asString() ?: "" else ""
            val typeName = fqName.shortName().asString()

            return builder.irCallConstructor(symbols.reflectionSymbols.wasmTypeInfoData.constructors.first(), emptyList()).also {
                it.putValueArgument(0, typeId)
                it.putValueArgument(1, packageName.toIrConst(context.irBuiltIns.stringType))
                it.putValueArgument(2, typeName.toIrConst(context.irBuiltIns.stringType))
            }
        }
    }

    private fun getExternalKClassCtorArgument(type: IrType, builder: DeclarationIrBuilder): IrExpression {
        val klass = type.classOrNull?.owner ?: error("Invalid type")
        check(klass.kind != ClassKind.INTERFACE) { "External interface must not be a class literal" }
        val classGetClassFunction = context.mapping.wasmGetJsClass[klass]!!
        val wrappedGetClassIfAny = context.mapping.wasmJsInteropFunctionToWrapper[classGetClassFunction] ?: classGetClassFunction
        return builder.irCall(wrappedGetClassIfAny)
    }

    override fun lower(irFile: IrFile) {
        val builder = context.createIrBuilder(irFile.symbol)
        irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
            override fun visitCall(expression: IrCall): IrExpression {
                val newExpression = transformCall(expression, builder)
                newExpression.transformChildrenVoid(this)
                return newExpression
            }
        })
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy