[JS IR BE] Eqeq operator lowering. IntrinsicifyCallsLowering.kt refactoring
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
+161
-106
@@ -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>()
|
||||||
|
|||||||
Reference in New Issue
Block a user