[JS IR BE] Eqeq operator lowering. IntrinsicifyCallsLowering.kt refactoring

This commit is contained in:
Svyatoslav Kuzmich
2018-07-04 18:10:10 +03:00
parent d412b6f4d0
commit a2f89cc711
3 changed files with 161 additions and 109 deletions
@@ -93,7 +93,6 @@ class JsIntrinsics(
// Number conversions: // Number conversions:
val jsAsIs = getInternalFunction("asIs") // as-is conversion. Call can be replaced with first paramenter
val jsNumberToByte = getInternalFunction("numberToByte") val jsNumberToByte = getInternalFunction("numberToByte")
val jsNumberToDouble = getInternalFunction("numberToDouble") val jsNumberToDouble = getInternalFunction("numberToDouble")
val jsNumberToInt = getInternalFunction("numberToInt") val jsNumberToInt = getInternalFunction("numberToInt")
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.utils.isNullable import org.jetbrains.kotlin.backend.common.utils.isNullable
import org.jetbrains.kotlin.backend.common.utils.isSubtypeOf
import org.jetbrains.kotlin.backend.common.utils.isSubtypeOfClass import org.jetbrains.kotlin.backend.common.utils.isSubtypeOfClass
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
@@ -14,6 +15,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.ConversionNames
import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
import org.jetbrains.kotlin.ir.backend.js.utils.isFakeOverriddenFromAny import org.jetbrains.kotlin.ir.backend.js.utils.isFakeOverriddenFromAny
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
@@ -23,30 +25,32 @@ import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.util.isNullConst
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.SimpleType import org.jetbrains.kotlin.types.SimpleType
private typealias MemberToTransformer = MutableMap<SimpleMemberKey, (IrCall) -> IrExpression>
private typealias SymbolToTransformer = MutableMap<IrFunctionSymbol, (IrCall) -> IrExpression>
class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileLoweringPass { class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileLoweringPass {
private val intrinsics = context.intrinsics private val intrinsics = context.intrinsics
private val irBuiltIns = context.irBuiltIns private val irBuiltIns = context.irBuiltIns
// TODO: should/can we unify these maps? // TODO: should/can we unify these maps?
private val memberToTransformer: Map<SimpleMemberKey, (IrCall) -> IrExpression> private val memberToTransformer: MemberToTransformer
private val memberToIrFunction: Map<SimpleMemberKey, IrSimpleFunctionSymbol> private val symbolToTransformer: SymbolToTransformer
private val symbolToIrFunction: Map<IrFunctionSymbol, IrSimpleFunction> private val nameToTransformer: Map<Name, (IrCall) -> IrExpression>
private val nameToIrTransformer: Map<Name, (IrCall) -> IrExpression>
init { init {
memberToIrFunction = mutableMapOf() symbolToTransformer = mutableMapOf()
symbolToIrFunction = mutableMapOf()
memberToTransformer = mutableMapOf() memberToTransformer = mutableMapOf()
nameToIrTransformer = mutableMapOf() nameToTransformer = mutableMapOf()
val primitiveNumbers = context.irBuiltIns.run { listOf(intType, shortType, byteType, floatType, doubleType) } val primitiveNumbers = context.irBuiltIns.run { listOf(intType, shortType, byteType, floatType, doubleType) }
memberToIrFunction.run { memberToTransformer.run {
for (type in primitiveNumbers) { for (type in primitiveNumbers) {
op(type, OperatorNames.UNARY_PLUS, intrinsics.jsUnaryPlus) op(type, OperatorNames.UNARY_PLUS, intrinsics.jsUnaryPlus)
op(type, OperatorNames.UNARY_MINUS, intrinsics.jsUnaryMinus) op(type, OperatorNames.UNARY_MINUS, intrinsics.jsUnaryMinus)
@@ -77,18 +81,17 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
// TODO: Add Char and Number conversions // TODO: Add Char and Number conversions
irBuiltIns.byteType.let { irBuiltIns.byteType.let {
op(it, ConversionNames.TO_BYTE, intrinsics.jsAsIs) op(it, ConversionNames.TO_BYTE, ::useDispatchReceiver)
op(it, ConversionNames.TO_DOUBLE, intrinsics.jsAsIs) op(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
op(it, ConversionNames.TO_FLOAT, intrinsics.jsAsIs) op(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
op(it, ConversionNames.TO_INT, intrinsics.jsAsIs) op(it, ConversionNames.TO_INT, ::useDispatchReceiver)
op(it, ConversionNames.TO_SHORT, intrinsics.jsAsIs) op(it, ConversionNames.TO_SHORT, ::useDispatchReceiver)
op(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
} }
for (type in listOf(irBuiltIns.floatType, irBuiltIns.doubleType)) { for (type in listOf(irBuiltIns.floatType, irBuiltIns.doubleType)) {
op(type, ConversionNames.TO_BYTE, intrinsics.jsNumberToByte) op(type, ConversionNames.TO_BYTE, intrinsics.jsNumberToByte)
op(type, ConversionNames.TO_DOUBLE, intrinsics.jsAsIs) op(type, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
op(type, ConversionNames.TO_FLOAT, intrinsics.jsAsIs) op(type, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
op(type, ConversionNames.TO_INT, intrinsics.jsNumberToInt) op(type, ConversionNames.TO_INT, intrinsics.jsNumberToInt)
op(type, ConversionNames.TO_SHORT, intrinsics.jsNumberToShort) op(type, ConversionNames.TO_SHORT, intrinsics.jsNumberToShort)
op(type, ConversionNames.TO_LONG, intrinsics.jsNumberToLong) op(type, ConversionNames.TO_LONG, intrinsics.jsNumberToLong)
@@ -96,27 +99,25 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
irBuiltIns.intType.let { irBuiltIns.intType.let {
op(it, ConversionNames.TO_BYTE, intrinsics.jsToByte) op(it, ConversionNames.TO_BYTE, intrinsics.jsToByte)
op(it, ConversionNames.TO_DOUBLE, intrinsics.jsAsIs) op(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
op(it, ConversionNames.TO_FLOAT, intrinsics.jsAsIs) op(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
op(it, ConversionNames.TO_INT, intrinsics.jsAsIs) op(it, ConversionNames.TO_INT, ::useDispatchReceiver)
op(it, ConversionNames.TO_SHORT, intrinsics.jsToShort) op(it, ConversionNames.TO_SHORT, intrinsics.jsToShort)
op(it, ConversionNames.TO_LONG, intrinsics.jsToLong) op(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
} }
irBuiltIns.shortType.let { irBuiltIns.shortType.let {
op(it, ConversionNames.TO_BYTE, intrinsics.jsToByte) op(it, ConversionNames.TO_BYTE, intrinsics.jsToByte)
op(it, ConversionNames.TO_DOUBLE, intrinsics.jsAsIs) op(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
op(it, ConversionNames.TO_FLOAT, intrinsics.jsAsIs) op(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
op(it, ConversionNames.TO_INT, intrinsics.jsAsIs) op(it, ConversionNames.TO_INT, ::useDispatchReceiver)
op(it, ConversionNames.TO_SHORT, intrinsics.jsAsIs) op(it, ConversionNames.TO_SHORT, ::useDispatchReceiver)
op(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
} }
} }
symbolToIrFunction.run { symbolToTransformer.run {
add(irBuiltIns.eqeqeqSymbol, intrinsics.jsEqeqeq) add(irBuiltIns.eqeqeqSymbol, intrinsics.jsEqeqeq)
// TODO: implement it a right way add(irBuiltIns.eqeqSymbol, ::transformEqeqOperator)
add(irBuiltIns.eqeqSymbol, intrinsics.jsEquals.owner)
// TODO: implement it a right way // TODO: implement it a right way
add(irBuiltIns.ieee754equalsFunByOperandType, intrinsics.jsEqeqeq) add(irBuiltIns.ieee754equalsFunByOperandType, intrinsics.jsEqeqeq)
@@ -153,7 +154,7 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
} }
} }
nameToIrTransformer.run { nameToTransformer.run {
addWithPredicate( addWithPredicate(
Name.special(Namer.KCALLABLE_GET_NAME), Name.special(Namer.KCALLABLE_GET_NAME),
{ call -> { call ->
@@ -193,7 +194,7 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
{ call -> irCall(call, intrinsics.jsCompareTo, dispatchReceiverAsFirstArgument = true) } { call -> irCall(call, intrinsics.jsCompareTo, dispatchReceiverAsFirstArgument = true) }
) )
put(Name.identifier("equals"), ::transformEquals) put(Name.identifier("equals"), ::transformEqualsMethodCall)
} }
} }
@@ -225,20 +226,8 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
if (call is IrCall) { if (call is IrCall) {
val symbol = call.symbol val symbol = call.symbol
// TODO create another map for this? Lower == to equals beforehand? symbolToTransformer[symbol]?.let {
if (symbol == irBuiltIns.eqeqSymbol) { return it(call)
val lhs = call.getValueArgument(0)!!
val rhs = call.getValueArgument(1)!!
return when (translateEquals(lhs.type, rhs.type)) {
is IdentityOperator -> irCall(call, intrinsics.jsEqeqeq.symbol)
is EqualityOperator -> irCall(call, intrinsics.jsEqeq.symbol)
else -> irCall(call, intrinsics.jsEquals)
}
}
symbolToIrFunction[symbol]?.let {
return irCall(call, it.symbol)
} }
// TODO: get rid of unbound symbols // TODO: get rid of unbound symbols
@@ -246,21 +235,12 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
(symbol.owner as? IrFunction)?.dispatchReceiverParameter?.let { (symbol.owner as? IrFunction)?.dispatchReceiverParameter?.let {
val key = SimpleMemberKey(it.type, symbol.owner.name) val key = SimpleMemberKey(it.type, symbol.owner.name)
memberToIrFunction[key]?.let {
if (it == intrinsics.jsAsIs) {
return call.dispatchReceiver!!
}
// TODO: don't apply intrinsics when type of receiver or argument is Long
return irCall(call, it, dispatchReceiverAsFirstArgument = true)
}
memberToTransformer[key]?.let { memberToTransformer[key]?.let {
return it(call) return it(call)
} }
} }
nameToIrTransformer[symbol.owner.name]?.let { nameToTransformer[symbol.owner.name]?.let {
return it(call) return it(call)
} }
} }
@@ -271,6 +251,56 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
}, null) }, null)
} }
private fun useDispatchReceiver(call: IrCall): IrExpression {
return call.dispatchReceiver!!
}
private fun transformEqeqOperator(call: IrCall): IrExpression {
val lhs = call.getValueArgument(0)!!
val rhs = call.getValueArgument(1)!!
// Special optimization for "<expression> == null"
if (lhs.isNullConst() || rhs.isNullConst())
return irCall(call, intrinsics.jsEqeq.symbol)
return when (translateEquals(lhs.type, rhs.type)) {
is IdentityOperator -> irCall(call, intrinsics.jsEqeqeq.symbol)
is EqualityOperator -> irCall(call, intrinsics.jsEqeq.symbol)
is RuntimeFunctionCall -> irCall(call, intrinsics.jsEquals)
is RuntimeOrMethodCall -> {
assert(!lhs.type.isNullable())
val equalsMethod = lhs.type.findEqualsMethod(rhs.type)
if (equalsMethod != null) {
irCall(call, equalsMethod.symbol, firstArgumentAsDispatchReceiver = true)
} else {
irCall(call, intrinsics.jsEquals)
}
}
}
}
private fun IrType.findEqualsMethod(rhs: IrType): IrSimpleFunction? {
val classifier = classifierOrNull ?: return null
if (!classifier.isBound) return null
return ((classifier.owner as? IrClass) ?: return null).declarations
.filterIsInstance<IrSimpleFunction>()
.filter {
it.name == Name.identifier("equals")
&& it.valueParameters.size == 1
&& rhs.isSubtypeOf(it.valueParameters[0].type)
&& !it.descriptor.isFakeOverriddenFromAny()
}
.maxWith( // Find the most specific function
Comparator { f1, f2 ->
val t1 = f1.valueParameters[0].type
val t2 = f2.valueParameters[0].type
if (t1.isSubtypeOf(t2)) {
if (t2.isSubtypeOf(t1)) 0 else 1
} else -1
}
)
}
private fun withLongCoercion(intrinsic: IrSimpleFunction): (IrCall) -> IrExpression = { call -> private fun withLongCoercion(intrinsic: IrSimpleFunction): (IrCall) -> IrExpression = { call ->
assert(call.valueArgumentsCount == 1) assert(call.valueArgumentsCount == 1)
val arg = call.getValueArgument(0)!! val arg = call.getValueArgument(0)!!
@@ -323,7 +353,7 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL
} }
} }
private fun transformEquals(call: IrCall): IrExpression { private fun transformEqualsMethodCall(call: IrCall): IrExpression {
if (call.superQualifier != null) return call if (call.superQualifier != null) return call
val symbol = call.symbol val symbol = call.symbol
if (!symbol.isBound) return call if (!symbol.isBound) return call
@@ -356,8 +386,7 @@ fun shouldReplaceToStringWithRuntimeCall(call: IrCall): Boolean {
}?.type ?: return false }?.type ?: return false
return receiverParameterType.run { return receiverParameterType.run {
this.isArray() || isArray() || isAny() || isNullable() || this is IrDynamicType || isString()
this.isAny() || this.isNullable() || this is IrDynamicType || this.isString()
} }
} }
@@ -397,65 +426,67 @@ JsNumber -- type lowered to JS Number
*/ */
sealed class EqualityLoweringType sealed class EqualityLoweringType
class IdentityOperator : EqualityLoweringType() object IdentityOperator : EqualityLoweringType()
class EqualityOperator : EqualityLoweringType() object EqualityOperator : EqualityLoweringType()
class RuntimeFunctionCall : EqualityLoweringType() object RuntimeFunctionCall : EqualityLoweringType()
class RuntimeOrMethodCall : EqualityLoweringType() object RuntimeOrMethodCall : EqualityLoweringType()
fun translateEquals(lhs: IrType, rhs: IrType): EqualityLoweringType = when { fun translateEquals(lhs: IrType, rhs: IrType): EqualityLoweringType = when {
lhs is IrDynamicType -> EqualityOperator
lhs.isJsNumber() -> translateEqualsForJsNumber(rhs) lhs.isJsNumber() -> translateEqualsForJsNumber(rhs)
lhs.isNullableJsNumber() -> translateEqualsForNullableJsNumber(rhs) lhs.isNullableJsNumber() -> translateEqualsForNullableJsNumber(rhs)
lhs.isLong() -> translateEqualsForLong(rhs) lhs.isLong() -> translateEqualsForLong(rhs)
lhs.isNullableLong() -> translateEqualsForNullableLong(rhs) lhs.isNullableLong() -> translateEqualsForNullableLong(rhs)
lhs.isBoolean() -> translateEqualsForBoolean(rhs) lhs.isBoolean() -> translateEqualsForBoolean(rhs)
lhs.isNullableBoolean() -> translateEqualsForNullableBoolean(rhs) lhs.isNullableBoolean() -> translateEqualsForNullableBoolean(rhs)
else -> RuntimeOrMethodCall() lhs.isNullable() -> RuntimeFunctionCall
else -> RuntimeOrMethodCall
} }
fun translateEqualsForJsNumber(rhs: IrType): EqualityLoweringType = when { fun translateEqualsForJsNumber(rhs: IrType): EqualityLoweringType = when {
rhs.isJsNumber() || rhs.isNullableJsNumber() -> IdentityOperator() rhs.isJsNumber() || rhs.isNullableJsNumber() -> IdentityOperator
rhs.isLong() || rhs.isNullableLong() -> EqualityOperator() rhs.isLong() || rhs.isNullableLong() -> EqualityOperator
rhs.isBoolean() || rhs.isNullableBoolean() -> IdentityOperator() rhs.isBoolean() || rhs.isNullableBoolean() -> IdentityOperator
else -> RuntimeFunctionCall() else -> RuntimeFunctionCall
} }
fun translateEqualsForNullableJsNumber(rhs: IrType): EqualityLoweringType = when { fun translateEqualsForNullableJsNumber(rhs: IrType): EqualityLoweringType = when {
rhs.isJsNumber() -> IdentityOperator() rhs.isJsNumber() -> IdentityOperator
rhs.isNullableJsNumber() -> EqualityOperator() rhs.isNullableJsNumber() -> EqualityOperator
rhs.isLong() || rhs.isNullableLong() -> EqualityOperator() rhs.isLong() || rhs.isNullableLong() -> EqualityOperator
rhs.isBoolean() -> IdentityOperator() rhs.isBoolean() -> IdentityOperator
else -> RuntimeFunctionCall() else -> RuntimeFunctionCall
} }
fun translateEqualsForLong(rhs: IrType): EqualityLoweringType = when { fun translateEqualsForLong(rhs: IrType): EqualityLoweringType = when {
rhs.isJsNumber() || rhs.isNullableJsNumber() -> EqualityOperator() rhs.isJsNumber() || rhs.isNullableJsNumber() -> EqualityOperator
rhs.isLong() || rhs.isNullableLong() -> RuntimeFunctionCall() rhs.isLong() || rhs.isNullableLong() -> RuntimeFunctionCall
rhs.isBoolean() || rhs.isNullableBoolean() -> IdentityOperator() rhs.isBoolean() || rhs.isNullableBoolean() -> IdentityOperator
else -> RuntimeFunctionCall() else -> RuntimeFunctionCall
} }
fun translateEqualsForNullableLong(rhs: IrType): EqualityLoweringType = when { fun translateEqualsForNullableLong(rhs: IrType): EqualityLoweringType = when {
rhs.isJsNumber() || rhs.isNullableJsNumber() -> EqualityOperator() rhs.isJsNumber() || rhs.isNullableJsNumber() -> EqualityOperator
rhs.isLong() || rhs.isNullableLong() -> RuntimeFunctionCall() rhs.isLong() || rhs.isNullableLong() -> RuntimeFunctionCall
rhs.isBoolean() -> IdentityOperator() rhs.isBoolean() -> IdentityOperator
else -> RuntimeFunctionCall() else -> RuntimeFunctionCall
} }
fun translateEqualsForBoolean(rhs: IrType): EqualityLoweringType = when { fun translateEqualsForBoolean(rhs: IrType): EqualityLoweringType = when {
rhs.isJsNumber() || rhs.isNullableJsNumber() -> IdentityOperator() rhs.isJsNumber() || rhs.isNullableJsNumber() -> IdentityOperator
rhs.isLong() || rhs.isNullableLong() -> IdentityOperator() rhs.isLong() || rhs.isNullableLong() -> IdentityOperator
rhs.isBoolean() || rhs.isNullableBoolean() -> IdentityOperator() rhs.isBoolean() || rhs.isNullableBoolean() -> IdentityOperator
else -> RuntimeFunctionCall() else -> RuntimeFunctionCall
} }
fun translateEqualsForNullableBoolean(rhs: IrType): EqualityLoweringType = when { fun translateEqualsForNullableBoolean(rhs: IrType): EqualityLoweringType = when {
rhs.isJsNumber() -> IdentityOperator() rhs.isJsNumber() -> IdentityOperator
rhs.isNullableJsNumber() -> RuntimeFunctionCall() rhs.isNullableJsNumber() -> RuntimeFunctionCall
rhs.isLong() -> IdentityOperator() rhs.isLong() -> IdentityOperator
rhs.isNullableLong() -> RuntimeFunctionCall() rhs.isNullableLong() -> RuntimeFunctionCall
rhs.isBoolean() -> IdentityOperator() rhs.isBoolean() -> IdentityOperator
rhs.isNullableBoolean() -> EqualityOperator() rhs.isNullableBoolean() -> EqualityOperator
else -> RuntimeFunctionCall() else -> RuntimeFunctionCall
} }
@@ -465,7 +496,12 @@ private fun IrType.isJsNumber(): Boolean = isPrimitiveType() && !isLong()
// TODO extract to common place? // TODO extract to common place?
fun irCall(call: IrCall, newSymbol: IrFunctionSymbol, dispatchReceiverAsFirstArgument: Boolean = false): IrCall = fun irCall(
call: IrCall,
newSymbol: IrFunctionSymbol,
dispatchReceiverAsFirstArgument: Boolean = false,
firstArgumentAsDispatchReceiver: Boolean = false
): IrCall =
call.run { call.run {
IrCallImpl( IrCallImpl(
startOffset, startOffset,
@@ -477,38 +513,53 @@ fun irCall(call: IrCall, newSymbol: IrFunctionSymbol, dispatchReceiverAsFirstArg
origin, origin,
superQualifierSymbol superQualifierSymbol
).apply { ).apply {
copyTypeAndValueArgumentsFrom(call, dispatchReceiverAsFirstArgument) copyTypeAndValueArgumentsFrom(
call,
dispatchReceiverAsFirstArgument,
firstArgumentAsDispatchReceiver
)
} }
} }
// TODO extract to common place? // TODO extract to common place?
private fun IrCall.copyTypeAndValueArgumentsFrom(call: IrCall, dispatchReceiverAsFirstArgument: Boolean = false) { private fun IrCall.copyTypeAndValueArgumentsFrom(
call: IrCall,
dispatchReceiverAsFirstArgument: Boolean = false,
firstArgumentAsDispatchReceiver: Boolean = false
) {
copyTypeArgumentsFrom(call) copyTypeArgumentsFrom(call)
var j = 0 var toValueArgumentIndex = 0
var fromValueArgumentIndex = 0
if (!dispatchReceiverAsFirstArgument) { when {
dispatchReceiver = call.dispatchReceiver dispatchReceiverAsFirstArgument -> {
} else { putValueArgument(toValueArgumentIndex++, call.dispatchReceiver)
putValueArgument(j++, call.dispatchReceiver) }
firstArgumentAsDispatchReceiver -> {
dispatchReceiver = call.getValueArgument(fromValueArgumentIndex++)
}
else -> {
dispatchReceiver = call.dispatchReceiver
}
} }
extensionReceiver = call.extensionReceiver extensionReceiver = call.extensionReceiver
for (i in 0 until call.valueArgumentsCount) { while (fromValueArgumentIndex < call.valueArgumentsCount) {
putValueArgument(j++, call.getValueArgument(i)) putValueArgument(toValueArgumentIndex++, call.getValueArgument(fromValueArgumentIndex++))
} }
} }
private fun MutableMap<SimpleMemberKey, IrSimpleFunctionSymbol>.op(type: IrType, name: Name, v: IrSimpleFunctionSymbol) { private fun MemberToTransformer.op(type: IrType, name: Name, v: IrSimpleFunctionSymbol) {
put(SimpleMemberKey(type, name), v) op(type, name, v = { irCall(it, v, dispatchReceiverAsFirstArgument = true) })
} }
private fun MutableMap<SimpleMemberKey, IrSimpleFunctionSymbol>.op(type: IrType, name: Name, v: IrSimpleFunction) { private fun MemberToTransformer.op(type: IrType, name: Name, v: IrSimpleFunction) {
put(SimpleMemberKey(type, name), v.symbol) op(type, name, v.symbol)
} }
private fun MutableMap<SimpleMemberKey, (IrCall) -> IrExpression>.op(type: IrType, name: Name, v: (IrCall) -> IrExpression) { private fun MemberToTransformer.op(type: IrType, name: Name, v: (IrCall) -> IrExpression) {
put(SimpleMemberKey(type, name), v) put(SimpleMemberKey(type, name), v)
} }
@@ -518,16 +569,20 @@ private fun <V> MutableMap<SimpleMemberKey, V>.op(type: IrType, name: String, v:
put(SimpleMemberKey(type, Name.identifier(name)), v) put(SimpleMemberKey(type, Name.identifier(name)), v)
} }
private fun <V> MutableMap<IrFunctionSymbol, V>.add(from: Map<SimpleType, IrSimpleFunction>, to: V) { private fun SymbolToTransformer.add(from: Map<SimpleType, IrSimpleFunction>, to: IrSimpleFunction) {
from.forEach { _, func -> from.forEach { _, func ->
add(func.symbol, to) add(func.symbol, to)
} }
} }
private fun <V> MutableMap<IrFunctionSymbol, V>.add(from: IrFunctionSymbol, to: V) { private fun SymbolToTransformer.add(from: IrFunctionSymbol, to: (IrCall) -> IrExpression) {
put(from, to) put(from, to)
} }
private fun SymbolToTransformer.add(from: IrFunctionSymbol, to: IrSimpleFunction) {
put(from, { call -> irCall(call, to.symbol) })
}
private fun <K> MutableMap<K, (IrCall) -> IrExpression>.addWithPredicate( private fun <K> MutableMap<K, (IrCall) -> IrExpression>.addWithPredicate(
from: K, from: K,
predicate: (IrCall) -> Boolean, predicate: (IrCall) -> Boolean,
@@ -5,8 +5,6 @@
package kotlin.js package kotlin.js
fun asIs(a: dynamic) = a
fun numberToByte(a: dynamic): Byte = toByte(numberToInt(a)) fun numberToByte(a: dynamic): Byte = toByte(numberToInt(a))
fun numberToDouble(a: dynamic): Double = js("+a").unsafeCast<Double>() fun numberToDouble(a: dynamic): Double = js("+a").unsafeCast<Double>()