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

org.jetbrains.kotlin.backend.jvm.ir.IrArrayBuilder.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.jvm.ir

import org.jetbrains.kotlin.backend.jvm.unboxInlineClass
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.getPropertyGetter

inline fun JvmIrBuilder.irArray(arrayType: IrType, block: IrArrayBuilder.() -> Unit): IrExpression =
    IrArrayBuilder(this, arrayType).apply { block() }.build()

fun JvmIrBuilder.irArrayOf(arrayType: IrType, elements: List = listOf()): IrExpression =
    irArray(arrayType) { elements.forEach { +it } }

private class IrArrayElement(val expression: IrExpression, val isSpread: Boolean)

class IrArrayBuilder(val builder: JvmIrBuilder, val arrayType: IrType) {
    // We build unboxed arrays for inline classes (UIntArray, etc) by first building
    // an unboxed array of the underlying primitive type, then coercing the result
    // to the correct type.
    val unwrappedArrayType = arrayType.unboxInlineClass()

    // Check if the array type is an inline class wrapper (UIntArray, etc.)
    val isUnboxedInlineClassArray
        get() = arrayType !== unwrappedArrayType

    // The unwrapped element type
    val elementType = unwrappedArrayType.getArrayElementType(builder.context.irBuiltIns)

    private val elements: MutableList = mutableListOf()

    private val hasSpread
        get() = elements.any { it.isSpread }

    operator fun IrExpression.unaryPlus() = add(this)
    fun add(expression: IrExpression) = elements.add(IrArrayElement(expression, false))

    fun addSpread(expression: IrExpression) = elements.add(IrArrayElement(expression, true))

    fun build(): IrExpression {
        val array = when {
            elements.isEmpty() -> newArray(0)
            !hasSpread -> buildSimpleArray()
            elements.size == 1 -> copyArray(elements.single().expression)
            else -> buildComplexArray()
        }
        return coerce(array, arrayType)
    }

    // Construct a new array of the specified size
    private fun newArray(size: Int) = newArray(builder.irInt(size))

    private fun newArray(size: IrExpression): IrExpression {
        val arrayConstructor = if (unwrappedArrayType.isBoxedArray)
            builder.irSymbols.arrayOfNulls
        else
            unwrappedArrayType.classOrNull!!.constructors.single { it.owner.valueParameters.size == 1 }

        return builder.irCall(arrayConstructor, unwrappedArrayType).apply {
            if (typeArgumentsCount != 0)
                putTypeArgument(0, elementType)
            putValueArgument(0, size)
        }
    }

    // Build an array without spreads
    private fun buildSimpleArray(): IrExpression =
        builder.irBlock {
            val result = irTemporary(newArray(elements.size))

            val set = unwrappedArrayType.classOrNull!!.functions.single {
                it.owner.name.asString() == "set"
            }

            for ((index, element) in elements.withIndex()) {
                +irCall(set).apply {
                    dispatchReceiver = irGet(result)
                    putValueArgument(0, irInt(index))
                    putValueArgument(1, coerce(element.expression, elementType))
                }
            }

            +irGet(result)
        }

    // Copy a single spread expression, unless it refers to a newly constructed array.
    private fun copyArray(spread: IrExpression): IrExpression {
        if (spread is IrConstructorCall ||
            (spread is IrFunctionAccessExpression && spread.symbol == builder.irSymbols.arrayOfNulls))
            return spread

        return builder.irBlock {
            val spreadVar = if (spread is IrGetValue) spread.symbol.owner else irTemporary(spread)
            val size = unwrappedArrayType.classOrNull!!.getPropertyGetter("size")!!
            val arrayCopyOf = builder.irSymbols.getArraysCopyOfFunction(unwrappedArrayType as IrSimpleType)
            // TODO consider using System.arraycopy if the requested array type is non-generic.
            +irCall(arrayCopyOf).apply {
                putValueArgument(0, coerce(irGet(spreadVar), unwrappedArrayType))
                putValueArgument(1, irCall(size).apply { dispatchReceiver = irGet(spreadVar) })
            }
        }
    }

    // Build an array containing spread expressions.
    private fun buildComplexArray(): IrExpression {
        val spreadBuilder = if (unwrappedArrayType.isBoxedArray)
            builder.irSymbols.spreadBuilder
        else
            builder.irSymbols.primitiveSpreadBuilders.getValue(elementType)

        val addElement = spreadBuilder.functions.single { it.owner.name.asString() == "add" }
        val addSpread = spreadBuilder.functions.single { it.owner.name.asString() == "addSpread" }
        val toArray = spreadBuilder.functions.single { it.owner.name.asString() == "toArray" }

        return builder.irBlock {
            val spreadBuilderVar = irTemporary(irCallConstructor(spreadBuilder.constructors.single(), listOf()).apply {
                putValueArgument(0, irInt(elements.size))
            })

            for (element in elements) {
                +irCall(if (element.isSpread) addSpread else addElement).apply {
                    dispatchReceiver = irGet(spreadBuilderVar)
                    putValueArgument(0, coerce(element.expression, if (element.isSpread) unwrappedArrayType else elementType))
                }
            }

            val toArrayCall = irCall(toArray).apply {
                dispatchReceiver = irGet(spreadBuilderVar)
                if (unwrappedArrayType.isBoxedArray) {
                    val size = spreadBuilder.functions.single { it.owner.name.asString() == "size" }
                    putValueArgument(0, irCall(builder.irSymbols.arrayOfNulls, arrayType).apply {
                        putTypeArgument(0, elementType)
                        putValueArgument(0, irCall(size).apply {
                            dispatchReceiver = irGet(spreadBuilderVar)
                        })
                    })
                }
            }

            if (unwrappedArrayType.isBoxedArray)
                +builder.irImplicitCast(toArrayCall, unwrappedArrayType)
            else
                +toArrayCall
        }
    }

    // Coerce expression to irType if we are working with an inline class array type
    private fun coerce(expression: IrExpression, irType: IrType): IrExpression =
        if (isUnboxedInlineClassArray)
            builder.irCall(builder.irSymbols.unsafeCoerceIntrinsic, irType).apply {
                putTypeArgument(0, expression.type)
                putTypeArgument(1, irType)
                putValueArgument(0, expression)
            }
        else expression
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy