[JS IR] Extract varargs in bridges generated for external methods
The issue this commit fixes occurs when we have an external interface
implemented by a Kotlin class, if that interface has methods with
varargs.
Kotlin functions expect varargs passed as arrays, but JavaScript code
may be unaware of this convention.
So, when generating a bridge for external interface method
implementaion, we insert some additional logic for extracting varargs
using the JavaScript `arguments` object.
A simplified example:
```kotlin
external interface Adder {
fun sum(vararg numbers: Int): Int
}
class AdderImpl: Adder {
override fun sum(vararg numbers: Int) = numbers.sum()
}
```
For `AdderImpl` we generate the following JS code:
```js
AdderImpl.prototype.sum_69wd7h_k$ = function (numbers) {
return sum(numbers);
};
AdderImpl.prototype.sum = function () {
var numbers = new Int32Array([].slice.call(arguments));
return this.sum_69wd7h_k$(numbers);
};
```
#KT-15223 Fixed
This commit is contained in:
+55
-28
@@ -13,41 +13,54 @@ import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.varargParameterIndex
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
|
||||
// Constructs bridges for inherited generic functions
|
||||
//
|
||||
// Example: for given class hierarchy
|
||||
//
|
||||
// class C<T> {
|
||||
// fun foo(t: T) = ...
|
||||
// }
|
||||
//
|
||||
// class D : C<Int> {
|
||||
// override fun foo(t: Int) = impl
|
||||
// }
|
||||
//
|
||||
// it adds method D that delegates generic calls to implementation:
|
||||
//
|
||||
// class D : C<Int> {
|
||||
// override fun foo(t: Int) = impl
|
||||
// fun foo(t: Any?) = foo(t as Int) // Constructed bridge
|
||||
// }
|
||||
//
|
||||
/**
|
||||
* Constructs bridges for inherited generic functions
|
||||
*
|
||||
* Example: for given class hierarchy
|
||||
*
|
||||
* class C<T> {
|
||||
* fun foo(t: T) = ...
|
||||
* }
|
||||
*
|
||||
* class D : C<Int> {
|
||||
* override fun foo(t: Int) = impl
|
||||
* }
|
||||
*
|
||||
* it adds method D that delegates generic calls to implementation:
|
||||
*
|
||||
* class D : C<Int> {
|
||||
* override fun foo(t: Int) = impl
|
||||
* fun foo(t: Any?) = foo(t as Int) // Constructed bridge
|
||||
* }
|
||||
*/
|
||||
abstract class BridgesConstruction<T : JsCommonBackendContext>(val context: T) : DeclarationTransformer {
|
||||
|
||||
private val specialBridgeMethods = SpecialBridgeMethods(context)
|
||||
|
||||
abstract fun getFunctionSignature(function: IrSimpleFunction): Any
|
||||
|
||||
/**
|
||||
* Usually just returns [irFunction]'s value parameters, but special transformations may be required if,
|
||||
* for example, we're dealing with an external function, and that function contains a vararg,
|
||||
* which we must extract and convert to an array.
|
||||
*/
|
||||
protected open fun extractValueParameters(
|
||||
blockBodyBuilder: IrBlockBodyBuilder,
|
||||
irFunction: IrSimpleFunction,
|
||||
bridge: IrSimpleFunction
|
||||
): List<IrValueDeclaration> = irFunction.valueParameters
|
||||
|
||||
// Should dispatch receiver type be casted inside a bridge.
|
||||
open val shouldCastDispatchReceiver: Boolean = false
|
||||
|
||||
@@ -131,7 +144,7 @@ abstract class BridgesConstruction<T : JsCommonBackendContext>(val context: T) :
|
||||
copyTypeParametersFrom(bridge)
|
||||
val substitutionMap = makeTypeParameterSubstitutionMap(bridge, this)
|
||||
copyReceiverParametersFrom(bridge, substitutionMap)
|
||||
valueParameters += bridge.valueParameters.map { p -> p.copyTo(this, type = p.type.substitute(substitutionMap)) }
|
||||
copyValueParametersFrom(bridge, substitutionMap)
|
||||
annotations += bridge.annotations
|
||||
overriddenSymbols += delegateTo.overriddenSymbols
|
||||
overriddenSymbols += bridge.symbol
|
||||
@@ -139,11 +152,12 @@ abstract class BridgesConstruction<T : JsCommonBackendContext>(val context: T) :
|
||||
|
||||
irFunction.body = context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET) {
|
||||
statements += context.createIrBuilder(irFunction.symbol).irBlockBody(irFunction) {
|
||||
val valueParameters = extractValueParameters(this, irFunction, bridge)
|
||||
if (specialMethodInfo != null) {
|
||||
irFunction.valueParameters.take(specialMethodInfo.argumentsToCheck).forEach {
|
||||
valueParameters.take(specialMethodInfo.argumentsToCheck).forEachIndexed { index, valueDeclaration ->
|
||||
+irIfThen(
|
||||
context.irBuiltIns.unitType,
|
||||
irNot(irIs(irGet(it), delegateTo.valueParameters[it.index].type)),
|
||||
irNot(irIs(irGet(valueDeclaration), delegateTo.valueParameters[index].type)),
|
||||
irReturn(specialMethodInfo.defaultValueGenerator(irFunction))
|
||||
)
|
||||
}
|
||||
@@ -161,9 +175,9 @@ abstract class BridgesConstruction<T : JsCommonBackendContext>(val context: T) :
|
||||
call.extensionReceiver = irCastIfNeeded(irGet(it), delegateTo.extensionReceiverParameter!!.type)
|
||||
}
|
||||
|
||||
val toTake = irFunction.valueParameters.size - if (call.isSuspend xor irFunction.isSuspend) 1 else 0
|
||||
val toTake = valueParameters.size - if (call.isSuspend xor irFunction.isSuspend) 1 else 0
|
||||
|
||||
irFunction.valueParameters.subList(0, toTake).mapIndexed { i, valueParameter ->
|
||||
valueParameters.subList(0, toTake).mapIndexed { i, valueParameter ->
|
||||
call.putValueArgument(i, irCastIfNeeded(irGet(valueParameter), delegateTo.valueParameters[i].type))
|
||||
}
|
||||
|
||||
@@ -174,6 +188,22 @@ abstract class BridgesConstruction<T : JsCommonBackendContext>(val context: T) :
|
||||
return irFunction
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the value parameters from [bridge] to [this]. If [bridge] is external and contains a vararg parameter,
|
||||
* only copies the parameters before the vararg.
|
||||
* The rest parameters are expected to be obtained later using the `arguments` object in JS.
|
||||
*/
|
||||
private fun IrSimpleFunction.copyValueParametersFrom(bridge: IrSimpleFunction, substitutionMap: Map<IrTypeParameterSymbol, IrType>) {
|
||||
var valueParametersToCopy = bridge.valueParameters
|
||||
if (bridge.isEffectivelyExternal()) {
|
||||
val varargIndex = bridge.varargParameterIndex()
|
||||
if (varargIndex != -1) {
|
||||
valueParametersToCopy = bridge.valueParameters.take(varargIndex)
|
||||
}
|
||||
}
|
||||
valueParameters += valueParametersToCopy.map { p -> p.copyTo(this, type = p.type.substitute(substitutionMap)) }
|
||||
}
|
||||
|
||||
abstract fun getBridgeOrigin(bridge: IrSimpleFunction): IrDeclarationOrigin
|
||||
|
||||
// TODO: get rid of Unit check
|
||||
@@ -213,6 +243,3 @@ data class IrBasedFunctionHandle(val function: IrSimpleFunction) : FunctionHandl
|
||||
override fun getOverridden() =
|
||||
function.overriddenSymbols.map { IrBasedFunctionHandle(it.owner) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+127
-2
@@ -7,18 +7,34 @@ package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrArithBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.varargParameterIndex
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.eraseGenerics
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.hasStableJsName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.types.isUnit
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class JsBridgesConstruction(context: JsIrBackendContext) : BridgesConstruction<JsIrBackendContext>(context) {
|
||||
|
||||
private val calculator = JsIrArithBuilder(context)
|
||||
|
||||
private val jsArguments = context.intrinsics.jsArguments
|
||||
private val jsArrayGet = context.intrinsics.jsArrayGet
|
||||
private val jsArrayLength = context.intrinsics.jsArrayLength
|
||||
private val jsArrayLike2Array = context.intrinsics.jsArrayLike2Array
|
||||
private val jsSliceArrayLikeFromIndex = context.intrinsics.jsSliceArrayLikeFromIndex
|
||||
private val jsSliceArrayLikeFromIndexToIndex = context.intrinsics.jsSliceArrayLikeFromIndexToIndex
|
||||
private val primitiveArrays = context.intrinsics.primitiveArrays
|
||||
private val primitiveToLiteralConstructor = context.intrinsics.primitiveToLiteralConstructor
|
||||
|
||||
private fun IrType.getJsInlinedClass() = context.inlineClassesUtils.getInlinedClass(this)
|
||||
|
||||
override fun getFunctionSignature(function: IrSimpleFunction): JsSignature =
|
||||
@@ -40,6 +56,115 @@ class JsBridgesConstruction(context: JsIrBackendContext) : BridgesConstruction<J
|
||||
JsLoweredDeclarationOrigin.BRIDGE_WITH_STABLE_NAME
|
||||
else
|
||||
JsLoweredDeclarationOrigin.BRIDGE_WITHOUT_STABLE_NAME
|
||||
|
||||
override fun extractValueParameters(
|
||||
blockBodyBuilder: IrBlockBodyBuilder,
|
||||
irFunction: IrSimpleFunction,
|
||||
bridge: IrSimpleFunction
|
||||
): List<IrValueDeclaration> {
|
||||
|
||||
if (!bridge.isEffectivelyExternal())
|
||||
return super.extractValueParameters(blockBodyBuilder, irFunction, bridge)
|
||||
|
||||
val varargIndex = bridge.varargParameterIndex()
|
||||
|
||||
if (varargIndex == -1)
|
||||
return super.extractValueParameters(blockBodyBuilder, irFunction, bridge)
|
||||
|
||||
return blockBodyBuilder.run {
|
||||
|
||||
// The number of parameters after the vararg
|
||||
val numberOfTrailingParameters = bridge.valueParameters.size - (varargIndex + 1)
|
||||
|
||||
val getTotalNumberOfArguments = irCall(jsArrayLength).apply {
|
||||
putValueArgument(0, irCall(jsArguments))
|
||||
type = context.irBuiltIns.intType
|
||||
}
|
||||
|
||||
val firstTrailingParameterIndexVar = lazy {
|
||||
irTemporary(
|
||||
if (numberOfTrailingParameters == 0)
|
||||
getTotalNumberOfArguments
|
||||
else
|
||||
calculator.sub(
|
||||
getTotalNumberOfArguments,
|
||||
irInt(numberOfTrailingParameters)
|
||||
),
|
||||
nameHint = "firstTrailingParameterIndex"
|
||||
)
|
||||
}
|
||||
|
||||
val varargArrayVar = emitCopyVarargToArray(
|
||||
bridge,
|
||||
varargIndex,
|
||||
numberOfTrailingParameters,
|
||||
firstTrailingParameterIndexVar
|
||||
)
|
||||
|
||||
val trailingParametersVars = createVarsForTrailingParameters(bridge, numberOfTrailingParameters, firstTrailingParameterIndexVar)
|
||||
|
||||
irFunction.valueParameters + varargArrayVar + trailingParametersVars
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.createVarsForTrailingParameters(
|
||||
bridge: IrSimpleFunction,
|
||||
numberOfTrailingParameters: Int,
|
||||
firstTrailingParameterIndexVar: Lazy<IrVariable>
|
||||
) = bridge.valueParameters.takeLast(numberOfTrailingParameters).mapIndexed { index, trailingParameter ->
|
||||
val parameterIndex = if (index == 0)
|
||||
irGet(firstTrailingParameterIndexVar.value)
|
||||
else
|
||||
calculator.add(irGet(firstTrailingParameterIndexVar.value), irInt(index))
|
||||
createTmpVariable(
|
||||
irCall(jsArrayGet).apply {
|
||||
putValueArgument(0, irCall(jsArguments))
|
||||
putValueArgument(1, parameterIndex)
|
||||
},
|
||||
nameHint = trailingParameter.name.asString(),
|
||||
irType = trailingParameter.type
|
||||
)
|
||||
}
|
||||
|
||||
private fun IrBlockBodyBuilder.emitCopyVarargToArray(
|
||||
bridge: IrSimpleFunction,
|
||||
varargIndex: Int,
|
||||
numberOfTrailingParameters: Int,
|
||||
firstTrailingParameterIndexVar: Lazy<IrVariable>
|
||||
): IrVariable {
|
||||
val varargElement = bridge.valueParameters[varargIndex]
|
||||
val sliceIntrinsicArgs = mutableListOf<IrExpression>(irCall(jsArguments))
|
||||
var sliceIntrinsic = jsArrayLike2Array
|
||||
if (varargIndex != 0 || numberOfTrailingParameters > 0) {
|
||||
sliceIntrinsicArgs.add(irInt(varargIndex))
|
||||
sliceIntrinsic = jsSliceArrayLikeFromIndex
|
||||
}
|
||||
if (numberOfTrailingParameters > 0) {
|
||||
sliceIntrinsicArgs.add(irGet(firstTrailingParameterIndexVar.value))
|
||||
sliceIntrinsic = jsSliceArrayLikeFromIndexToIndex
|
||||
}
|
||||
|
||||
val varargCopiedAsArray = irCall(sliceIntrinsic).apply {
|
||||
putTypeArgument(0, varargElement.varargElementType!!)
|
||||
sliceIntrinsicArgs.forEachIndexed(this::putValueArgument)
|
||||
}.let { arrayExpr ->
|
||||
val arrayInfo =
|
||||
InlineClassArrayInfo(this@JsBridgesConstruction.context, varargElement.varargElementType!!, varargElement.type)
|
||||
val primitiveType = primitiveArrays[arrayInfo.primitiveArrayType.classifierOrNull]
|
||||
if (primitiveType != null) {
|
||||
arrayInfo.boxArrayIfNeeded(
|
||||
irCall(primitiveToLiteralConstructor.getValue(primitiveType)).apply {
|
||||
putValueArgument(0, arrayExpr)
|
||||
type = varargElement.type
|
||||
}
|
||||
)
|
||||
} else {
|
||||
arrayExpr
|
||||
}
|
||||
}
|
||||
|
||||
return createTmpVariable(varargCopiedAsArray, nameHint = varargElement.name.asString())
|
||||
}
|
||||
}
|
||||
|
||||
interface JsSignature {
|
||||
|
||||
+87
-84
@@ -6,8 +6,10 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.compilationException
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
@@ -19,7 +21,6 @@ import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.getInlineClassBackingField
|
||||
import org.jetbrains.kotlin.ir.util.getInlineClassUnderlyingType
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
|
||||
@@ -34,92 +35,15 @@ private class VarargTransformer(
|
||||
val context: JsIrBackendContext
|
||||
) : IrElementTransformerVoid() {
|
||||
|
||||
fun IrType.getInlinedClass() = context.inlineClassesUtils.getInlinedClass(this)
|
||||
|
||||
var externalVarargs = mutableSetOf<IrVararg>()
|
||||
|
||||
private fun List<IrExpression>.toArrayLiteral(type: IrType, varargElementType: IrType): IrExpression {
|
||||
|
||||
// TODO: Use symbols when builtins symbol table is fixes
|
||||
val primitiveType = context.intrinsics.primitiveArrays.mapKeys { it.key }[type.classifierOrNull]
|
||||
|
||||
val intrinsic =
|
||||
if (primitiveType != null)
|
||||
context.intrinsics.primitiveToLiteralConstructor.getValue(primitiveType)
|
||||
else
|
||||
context.intrinsics.arrayLiteral
|
||||
|
||||
val startOffset = firstOrNull()?.startOffset ?: UNDEFINED_OFFSET
|
||||
val endOffset = lastOrNull()?.endOffset ?: UNDEFINED_OFFSET
|
||||
|
||||
val irVararg = IrVarargImpl(startOffset, endOffset, type, varargElementType, this)
|
||||
|
||||
return IrCallImpl(
|
||||
startOffset, endOffset,
|
||||
type, intrinsic,
|
||||
typeArgumentsCount = if (intrinsic.owner.typeParameters.isNotEmpty()) 1 else 0,
|
||||
valueArgumentsCount = 1
|
||||
).apply {
|
||||
if (typeArgumentsCount == 1) {
|
||||
putTypeArgument(0, varargElementType)
|
||||
}
|
||||
putValueArgument(0, irVararg)
|
||||
}
|
||||
}
|
||||
|
||||
inner class InlineClassArrayInfo(
|
||||
val elementType: IrType,
|
||||
val arrayType: IrType
|
||||
) {
|
||||
val arrayInlineClass = arrayType.getInlinedClass()
|
||||
val inlined = arrayInlineClass != null
|
||||
|
||||
val primitiveElementType = when {
|
||||
inlined -> getInlineClassUnderlyingType(elementType.getInlinedClass()!!)
|
||||
else -> elementType
|
||||
}
|
||||
|
||||
val primitiveArrayType = when {
|
||||
inlined -> getInlineClassUnderlyingType(arrayInlineClass!!)
|
||||
else -> arrayType
|
||||
}
|
||||
|
||||
fun boxArrayIfNeeded(array: IrExpression) =
|
||||
if (arrayInlineClass == null)
|
||||
array
|
||||
else with(array) {
|
||||
IrConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset,
|
||||
endOffset,
|
||||
arrayInlineClass.defaultType,
|
||||
arrayInlineClass.constructors.single { it.isPrimary }.symbol,
|
||||
arrayInlineClass.typeParameters.size
|
||||
).also {
|
||||
it.putValueArgument(0, array)
|
||||
}
|
||||
}
|
||||
|
||||
fun unboxElementIfNeeded(element: IrExpression): IrExpression {
|
||||
if (arrayInlineClass == null)
|
||||
return element
|
||||
else with(element) {
|
||||
val inlinedClass = type.getInlinedClass() ?: return element
|
||||
val field = getInlineClassBackingField(inlinedClass)
|
||||
return IrGetFieldImpl(startOffset, endOffset, field.symbol, field.type, this)
|
||||
}
|
||||
}
|
||||
|
||||
fun toPrimitiveArrayLiteral(elements: List<IrExpression>) =
|
||||
elements.toArrayLiteral(primitiveArrayType, primitiveElementType)
|
||||
}
|
||||
|
||||
override fun visitVararg(expression: IrVararg): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val currentList = mutableListOf<IrExpression>()
|
||||
val segments = mutableListOf<IrExpression>()
|
||||
|
||||
val arrayInfo = InlineClassArrayInfo(expression.varargElementType, expression.type)
|
||||
val arrayInfo = InlineClassArrayInfo(context, expression.varargElementType, expression.type)
|
||||
|
||||
for (e in expression.elements) {
|
||||
when (e) {
|
||||
@@ -162,6 +86,7 @@ private class VarargTransformer(
|
||||
|
||||
val arrayLiteral =
|
||||
segments.toArrayLiteral(
|
||||
context,
|
||||
IrSimpleTypeImpl(context.intrinsics.array, false, emptyList(), emptyList()), // TODO: Substitution
|
||||
context.irBuiltIns.anyType
|
||||
)
|
||||
@@ -218,7 +143,7 @@ private class VarargTransformer(
|
||||
} else segment
|
||||
}
|
||||
|
||||
private fun transformFunctionAccessExpression(expression: IrFunctionAccessExpression): IrExpression {
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression): IrExpression {
|
||||
expression.transformChildrenVoid()
|
||||
|
||||
if (expression.symbol.owner.isExternal) {
|
||||
@@ -240,7 +165,7 @@ private class VarargTransformer(
|
||||
val parameter = expression.symbol.owner.valueParameters[i]
|
||||
val varargElementType = parameter.varargElementType
|
||||
if (argument == null && varargElementType != null) {
|
||||
val arrayInfo = InlineClassArrayInfo(varargElementType, parameter.type)
|
||||
val arrayInfo = InlineClassArrayInfo(context, varargElementType, parameter.type)
|
||||
val emptyArray = with(arrayInfo) {
|
||||
boxArrayIfNeeded(toPrimitiveArrayLiteral(emptyList()))
|
||||
}
|
||||
@@ -251,7 +176,85 @@ private class VarargTransformer(
|
||||
|
||||
return expression
|
||||
}
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression) =
|
||||
transformFunctionAccessExpression(expression)
|
||||
}
|
||||
|
||||
private fun List<IrExpression>.toArrayLiteral(context: JsIrBackendContext, type: IrType, varargElementType: IrType): IrExpression {
|
||||
|
||||
// TODO: Use symbols when builtins symbol table is fixes
|
||||
val primitiveType = context.intrinsics.primitiveArrays.mapKeys { it.key }[type.classifierOrNull]
|
||||
|
||||
val intrinsic =
|
||||
if (primitiveType != null)
|
||||
context.intrinsics.primitiveToLiteralConstructor.getValue(primitiveType)
|
||||
else
|
||||
context.intrinsics.arrayLiteral
|
||||
|
||||
val startOffset = firstOrNull()?.startOffset ?: UNDEFINED_OFFSET
|
||||
val endOffset = lastOrNull()?.endOffset ?: UNDEFINED_OFFSET
|
||||
|
||||
val irVararg = IrVarargImpl(startOffset, endOffset, type, varargElementType, this)
|
||||
|
||||
return IrCallImpl(
|
||||
startOffset, endOffset,
|
||||
type, intrinsic,
|
||||
typeArgumentsCount = if (intrinsic.owner.typeParameters.isNotEmpty()) 1 else 0,
|
||||
valueArgumentsCount = 1
|
||||
).apply {
|
||||
if (typeArgumentsCount == 1) {
|
||||
putTypeArgument(0, varargElementType)
|
||||
}
|
||||
putValueArgument(0, irVararg)
|
||||
}
|
||||
}
|
||||
|
||||
internal class InlineClassArrayInfo(val context: JsIrBackendContext, val elementType: IrType, val arrayType: IrType) {
|
||||
|
||||
private fun IrType.getInlinedClass() = context.inlineClassesUtils.getInlinedClass(this)
|
||||
private fun getInlineClassUnderlyingType(irClass: IrClass) = context.inlineClassesUtils.getInlineClassUnderlyingType(irClass)
|
||||
|
||||
val arrayInlineClass = arrayType.getInlinedClass()
|
||||
val inlined = arrayInlineClass != null
|
||||
|
||||
val primitiveElementType = when {
|
||||
inlined -> getInlineClassUnderlyingType(
|
||||
elementType.getInlinedClass() ?: compilationException(
|
||||
"Could not get inlined class",
|
||||
elementType
|
||||
)
|
||||
)
|
||||
else -> elementType
|
||||
}
|
||||
|
||||
val primitiveArrayType = when {
|
||||
inlined -> getInlineClassUnderlyingType(arrayInlineClass!!)
|
||||
else -> arrayType
|
||||
}
|
||||
|
||||
fun boxArrayIfNeeded(array: IrExpression) =
|
||||
if (arrayInlineClass == null)
|
||||
array
|
||||
else with(array) {
|
||||
IrConstructorCallImpl.fromSymbolOwner(
|
||||
startOffset,
|
||||
endOffset,
|
||||
arrayInlineClass.defaultType,
|
||||
arrayInlineClass.constructors.single { it.isPrimary }.symbol,
|
||||
arrayInlineClass.typeParameters.size
|
||||
).also {
|
||||
it.putValueArgument(0, array)
|
||||
}
|
||||
}
|
||||
|
||||
fun unboxElementIfNeeded(element: IrExpression): IrExpression {
|
||||
if (arrayInlineClass == null)
|
||||
return element
|
||||
else with(element) {
|
||||
val inlinedClass = type.getInlinedClass() ?: return element
|
||||
val field = getInlineClassBackingField(inlinedClass)
|
||||
return IrGetFieldImpl(startOffset, endOffset, field.symbol, field.type, this)
|
||||
}
|
||||
}
|
||||
|
||||
fun toPrimitiveArrayLiteral(elements: List<IrExpression>) =
|
||||
elements.toArrayLiteral(context, primitiveArrayType, primitiveElementType)
|
||||
}
|
||||
|
||||
+3
@@ -319,6 +319,9 @@ fun argumentsWithVarargAsSingleArray(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the vararg parameter of the function if there is one, otherwise returns -1.
|
||||
*/
|
||||
fun IrFunction.varargParameterIndex() = valueParameters.indexOfFirst { it.varargElementType != null }
|
||||
|
||||
fun translateCallArguments(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
open class A<T> {
|
||||
open fun foo(t: T) = "A"
|
||||
open fun foo(t: T, vararg xs: Int) = "A"
|
||||
}
|
||||
|
||||
open class B : A<String>()
|
||||
|
||||
class Z : B() {
|
||||
override fun foo(t: String) = "Z"
|
||||
override fun foo(t: String, vararg xs: Int) = "Z"
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user