[JS IR BE] Support integer operation overflow
This commit is contained in:
@@ -143,6 +143,8 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
|||||||
val jsCompareTo = getInternalFunction("compareTo")
|
val jsCompareTo = getInternalFunction("compareTo")
|
||||||
val jsEquals = getInternalFunction("equals")
|
val jsEquals = getInternalFunction("equals")
|
||||||
|
|
||||||
|
val jsImul = getInternalFunction("imul")
|
||||||
|
|
||||||
// Coroutines
|
// Coroutines
|
||||||
|
|
||||||
val jsCoroutineContext = context.symbolTable.referenceSimpleFunction(context.coroutineContextProperty.getter!!)
|
val jsCoroutineContext = context.symbolTable.referenceSimpleFunction(context.coroutineContextProperty.getter!!)
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
|
|||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
@@ -233,6 +234,10 @@ object JsIrBuilder {
|
|||||||
fun buildTypeOperator(type: IrType, operator: IrTypeOperator, argument: IrExpression, toType: IrType, symbol: IrClassifierSymbol) =
|
fun buildTypeOperator(type: IrType, operator: IrTypeOperator, argument: IrExpression, toType: IrType, symbol: IrClassifierSymbol) =
|
||||||
IrTypeOperatorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, operator, toType, symbol, argument)
|
IrTypeOperatorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, operator, toType, symbol, argument)
|
||||||
|
|
||||||
|
fun buildImplicitCast(value: IrExpression, toType: IrType) =
|
||||||
|
JsIrBuilder.buildTypeOperator(toType, IrTypeOperator.IMPLICIT_CAST, value, toType, toType.classifierOrFail)
|
||||||
|
|
||||||
|
|
||||||
fun buildNull(type: IrType) = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type)
|
fun buildNull(type: IrType) = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type)
|
||||||
fun buildBoolean(type: IrType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
|
fun buildBoolean(type: IrType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
|
||||||
fun buildInt(type: IrType, v: Int) = IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
|
fun buildInt(type: IrType, v: Int) = IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
|
||||||
@@ -267,6 +272,16 @@ fun IrClass.simpleFunctions(): List<IrSimpleFunction> = this.declarations.flatMa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO extract to common place?
|
// TODO extract to common place?
|
||||||
|
|
||||||
|
fun irCall(
|
||||||
|
call: IrCall,
|
||||||
|
newFunction: IrFunction,
|
||||||
|
dispatchReceiverAsFirstArgument: Boolean = false,
|
||||||
|
firstArgumentAsDispatchReceiver: Boolean = false
|
||||||
|
): IrCall =
|
||||||
|
irCall(call, newFunction.symbol, dispatchReceiverAsFirstArgument, firstArgumentAsDispatchReceiver)
|
||||||
|
|
||||||
|
|
||||||
fun irCall(
|
fun irCall(
|
||||||
call: IrCall,
|
call: IrCall,
|
||||||
newSymbol: IrFunctionSymbol,
|
newSymbol: IrFunctionSymbol,
|
||||||
|
|||||||
+2
@@ -12,6 +12,7 @@ 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.expressions.IrCall
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.types.isLong
|
||||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
@@ -19,6 +20,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
|||||||
class CallsLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
class CallsLowering(val context: JsIrBackendContext) : FileLoweringPass {
|
||||||
private val transformers = listOf(
|
private val transformers = listOf(
|
||||||
NumberOperatorCallsTransformer(context),
|
NumberOperatorCallsTransformer(context),
|
||||||
|
NumberConversionCallsTransformer(context),
|
||||||
DynamicCallsTransformer(context),
|
DynamicCallsTransformer(context),
|
||||||
EqualityAndComparisonCallsTransformer(context),
|
EqualityAndComparisonCallsTransformer(context),
|
||||||
PrimitiveContainerMemberCallTransformer(context),
|
PrimitiveContainerMemberCallTransformer(context),
|
||||||
|
|||||||
+98
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o. 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.ir.backend.js.lower.calls
|
||||||
|
|
||||||
|
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.irCall
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.ConversionNames
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
|
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.IrCallImpl
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.constructors
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
class NumberConversionCallsTransformer(context: JsIrBackendContext) : CallsTransformer {
|
||||||
|
private val intrinsics = context.intrinsics
|
||||||
|
private val irBuiltIns = context.irBuiltIns
|
||||||
|
|
||||||
|
private val memberToTransformer = MemberToTransformer().apply {
|
||||||
|
// Conversion rules are ported from NumberAndCharConversionFIF
|
||||||
|
// TODO: Add Char and Number conversions
|
||||||
|
|
||||||
|
irBuiltIns.byteType.let {
|
||||||
|
add(it, ConversionNames.TO_BYTE, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_INT, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_SHORT, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (type in listOf(irBuiltIns.floatType, irBuiltIns.doubleType)) {
|
||||||
|
add(type, ConversionNames.TO_BYTE, intrinsics.jsNumberToByte)
|
||||||
|
add(type, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
|
||||||
|
add(type, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
|
||||||
|
add(type, ConversionNames.TO_INT, intrinsics.jsNumberToInt)
|
||||||
|
add(type, ConversionNames.TO_SHORT, intrinsics.jsNumberToShort)
|
||||||
|
add(type, ConversionNames.TO_LONG, intrinsics.jsNumberToLong)
|
||||||
|
}
|
||||||
|
|
||||||
|
irBuiltIns.intType.let {
|
||||||
|
add(it, ConversionNames.TO_BYTE, intrinsics.jsToByte)
|
||||||
|
add(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_INT, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_SHORT, intrinsics.jsToShort)
|
||||||
|
add(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
|
||||||
|
}
|
||||||
|
|
||||||
|
irBuiltIns.shortType.let {
|
||||||
|
add(it, ConversionNames.TO_BYTE, intrinsics.jsToByte)
|
||||||
|
add(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_INT, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_SHORT, ::useDispatchReceiver)
|
||||||
|
add(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (type in arrayOf(irBuiltIns.byteType, irBuiltIns.intType)) {
|
||||||
|
add(type, ConversionNames.TO_CHAR) {
|
||||||
|
irCall(it, intrinsics.charClassSymbol.constructors.single(), dispatchReceiverAsFirstArgument = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (type in arrayOf(irBuiltIns.floatType, irBuiltIns.doubleType)) {
|
||||||
|
add(type, ConversionNames.TO_CHAR) {
|
||||||
|
JsIrBuilder.buildCall(intrinsics.charClassSymbol.constructors.single()).apply {
|
||||||
|
putValueArgument(0, irCall(it, intrinsics.jsNumberToInt, dispatchReceiverAsFirstArgument = true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add(irBuiltIns.charType, ConversionNames.TO_CHAR) { it.dispatchReceiver!! }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun transformCall(call: IrCall): IrExpression {
|
||||||
|
val function = call.symbol.owner
|
||||||
|
function.dispatchReceiverParameter?.also {
|
||||||
|
val key = SimpleMemberKey(it.type, function.name)
|
||||||
|
memberToTransformer[key]?.also {
|
||||||
|
return it(call)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun useDispatchReceiver(call: IrCall): IrExpression {
|
||||||
|
return JsIrBuilder.buildImplicitCast(call.dispatchReceiver!!, call.type)
|
||||||
|
}
|
||||||
|
}
|
||||||
+107
-87
@@ -8,29 +8,29 @@ package org.jetbrains.kotlin.ir.backend.js.lower.calls
|
|||||||
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
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ir.irCall
|
import org.jetbrains.kotlin.ir.backend.js.ir.irCall
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.ConversionNames
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
|
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
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.util.constructors
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransformer {
|
class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransformer {
|
||||||
private val intrinsics = context.intrinsics
|
private val intrinsics = context.intrinsics
|
||||||
private val irBuiltIns = context.irBuiltIns
|
private val irBuiltIns = context.irBuiltIns
|
||||||
|
|
||||||
private val primitiveNumbers =
|
private fun buildInt(v: Int) = JsIrBuilder.buildInt(irBuiltIns.intType, v)
|
||||||
irBuiltIns.run { listOf(intType, shortType, byteType, floatType, doubleType) }
|
|
||||||
|
|
||||||
private val memberToTransformer = MemberToTransformer().apply {
|
private val memberToTransformer = MemberToTransformer().apply {
|
||||||
|
|
||||||
|
val primitiveNumbers =
|
||||||
|
irBuiltIns.run { listOf(intType, shortType, byteType, floatType, doubleType) }
|
||||||
|
|
||||||
for (type in primitiveNumbers) {
|
for (type in primitiveNumbers) {
|
||||||
add(type, OperatorNames.UNARY_PLUS, intrinsics.jsUnaryPlus)
|
add(type, OperatorNames.UNARY_PLUS, intrinsics.jsUnaryPlus)
|
||||||
add(type, OperatorNames.UNARY_MINUS, intrinsics.jsUnaryMinus)
|
add(type, OperatorNames.UNARY_MINUS, ::transformUnaryMinus)
|
||||||
}
|
}
|
||||||
|
|
||||||
add(irBuiltIns.stringType, OperatorNames.ADD, intrinsics.jsPlus)
|
add(irBuiltIns.stringType, OperatorNames.ADD, intrinsics.jsPlus)
|
||||||
@@ -52,87 +52,23 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo
|
|||||||
add(it, OperatorNames.XOR, intrinsics.jsBitXor)
|
add(it, OperatorNames.XOR, intrinsics.jsBitXor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conversion rules are ported from NumberAndCharConversionFIF
|
|
||||||
// TODO: Add Char and Number conversions
|
|
||||||
|
|
||||||
irBuiltIns.byteType.let {
|
|
||||||
add(it, ConversionNames.TO_BYTE, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_INT, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_SHORT, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (type in listOf(irBuiltIns.floatType, irBuiltIns.doubleType)) {
|
|
||||||
add(type, ConversionNames.TO_BYTE, intrinsics.jsNumberToByte)
|
|
||||||
add(type, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
|
|
||||||
add(type, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
|
|
||||||
add(type, ConversionNames.TO_INT, intrinsics.jsNumberToInt)
|
|
||||||
add(type, ConversionNames.TO_SHORT, intrinsics.jsNumberToShort)
|
|
||||||
add(type, ConversionNames.TO_LONG, intrinsics.jsNumberToLong)
|
|
||||||
}
|
|
||||||
|
|
||||||
irBuiltIns.intType.let {
|
|
||||||
add(it, ConversionNames.TO_BYTE, intrinsics.jsToByte)
|
|
||||||
add(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_INT, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_SHORT, intrinsics.jsToShort)
|
|
||||||
add(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
|
|
||||||
}
|
|
||||||
|
|
||||||
irBuiltIns.shortType.let {
|
|
||||||
add(it, ConversionNames.TO_BYTE, intrinsics.jsToByte)
|
|
||||||
add(it, ConversionNames.TO_DOUBLE, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_FLOAT, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_INT, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_SHORT, ::useDispatchReceiver)
|
|
||||||
add(it, ConversionNames.TO_LONG, intrinsics.jsToLong)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (type in primitiveNumbers) {
|
for (type in primitiveNumbers) {
|
||||||
add(type, Name.identifier("rangeTo"), ::transformRangeTo)
|
add(type, Name.identifier("rangeTo"), ::transformRangeTo)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (type in primitiveNumbers) {
|
for (type in primitiveNumbers) {
|
||||||
// TODO: use increment and decrement when it's possible
|
add(type, OperatorNames.INC, ::transformIncrement)
|
||||||
add(type, OperatorNames.INC) {
|
add(type, OperatorNames.DEC, ::transformDecrement)
|
||||||
irCall(it, intrinsics.jsPlus.symbol, dispatchReceiverAsFirstArgument = true).apply {
|
|
||||||
putValueArgument(1, JsIrBuilder.buildInt(irBuiltIns.intType, 1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
add(type, OperatorNames.DEC) {
|
|
||||||
irCall(it, intrinsics.jsMinus.symbol, dispatchReceiverAsFirstArgument = true).apply {
|
|
||||||
putValueArgument(1, JsIrBuilder.buildInt(irBuiltIns.intType, 1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (type in primitiveNumbers) {
|
for (type in primitiveNumbers) {
|
||||||
add(type, OperatorNames.ADD, withLongCoercion(intrinsics.jsPlus))
|
add(type, OperatorNames.ADD, withLongCoercion(::transformAdd))
|
||||||
add(type, OperatorNames.SUB, withLongCoercion(intrinsics.jsMinus))
|
add(type, OperatorNames.SUB, withLongCoercion(::transformSub))
|
||||||
add(type, OperatorNames.MUL, withLongCoercion(intrinsics.jsMult))
|
add(type, OperatorNames.MUL, withLongCoercion(::transformMul))
|
||||||
add(type, OperatorNames.DIV, withLongCoercion(intrinsics.jsDiv))
|
add(type, OperatorNames.DIV, withLongCoercion(::transformDiv))
|
||||||
add(type, OperatorNames.MOD, withLongCoercion(intrinsics.jsMod))
|
add(type, OperatorNames.MOD, withLongCoercion(::transformRem))
|
||||||
add(type, OperatorNames.REM, withLongCoercion(intrinsics.jsMod))
|
add(type, OperatorNames.REM, withLongCoercion(::transformRem))
|
||||||
}
|
}
|
||||||
|
|
||||||
for (type in arrayOf(irBuiltIns.byteType, irBuiltIns.intType)) {
|
|
||||||
add(type, ConversionNames.TO_CHAR) {
|
|
||||||
irCall(it, intrinsics.charClassSymbol.constructors.single(), dispatchReceiverAsFirstArgument = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (type in arrayOf(irBuiltIns.floatType, irBuiltIns.doubleType)) {
|
|
||||||
add(type, ConversionNames.TO_CHAR) {
|
|
||||||
JsIrBuilder.buildCall(intrinsics.charClassSymbol.constructors.single()).apply {
|
|
||||||
putValueArgument(0, irCall(it, intrinsics.jsNumberToInt, dispatchReceiverAsFirstArgument = true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add(irBuiltIns.charType, ConversionNames.TO_CHAR) { it.dispatchReceiver!! }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transformCall(call: IrCall): IrExpression {
|
override fun transformCall(call: IrCall): IrExpression {
|
||||||
@@ -146,10 +82,6 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo
|
|||||||
return call
|
return call
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun useDispatchReceiver(call: IrCall): IrExpression {
|
|
||||||
return call.dispatchReceiver!!
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun transformRangeTo(call: IrCall): IrExpression {
|
private fun transformRangeTo(call: IrCall): IrExpression {
|
||||||
if (call.valueArgumentsCount != 1) return call
|
if (call.valueArgumentsCount != 1) return call
|
||||||
return with(call.symbol.owner.valueParameters[0].type) {
|
return with(call.symbol.owner.valueParameters[0].type) {
|
||||||
@@ -163,7 +95,83 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun withLongCoercion(intrinsic: IrSimpleFunction): (IrCall) -> IrExpression = { call ->
|
private fun irBinaryOp(
|
||||||
|
call: IrCall,
|
||||||
|
intrinsic: IrFunction,
|
||||||
|
toInt32: Boolean = false
|
||||||
|
): IrExpression {
|
||||||
|
val newCall = irCall(call, intrinsic, dispatchReceiverAsFirstArgument = true)
|
||||||
|
if (toInt32)
|
||||||
|
return toInt32(newCall)
|
||||||
|
return newCall
|
||||||
|
}
|
||||||
|
|
||||||
|
class BinaryOp(call: IrCall) {
|
||||||
|
val function = call.symbol.owner
|
||||||
|
val name = function.name
|
||||||
|
val lhs = function.dispatchReceiverParameter!!.type
|
||||||
|
val rhs = function.valueParameters[0].type
|
||||||
|
val result = function.returnType
|
||||||
|
|
||||||
|
fun canAddOrSubOverflow() =
|
||||||
|
result.isInt() && (lhs.isInt() || rhs.isInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun transformAdd(call: IrCall) =
|
||||||
|
irBinaryOp(call, intrinsics.jsPlus, toInt32 = BinaryOp(call).canAddOrSubOverflow())
|
||||||
|
|
||||||
|
private fun transformSub(call: IrCall) =
|
||||||
|
irBinaryOp(call, intrinsics.jsMinus, toInt32 = BinaryOp(call).canAddOrSubOverflow())
|
||||||
|
|
||||||
|
private fun transformMul(call: IrCall) = BinaryOp(call).run {
|
||||||
|
when {
|
||||||
|
result.isInt() -> when {
|
||||||
|
|
||||||
|
lhs.isInt() && rhs.isInt() ->
|
||||||
|
irBinaryOp(call, intrinsics.jsImul.owner)
|
||||||
|
|
||||||
|
else ->
|
||||||
|
irBinaryOp(call, intrinsics.jsMult, toInt32 = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> irBinaryOp(call, intrinsics.jsMult, toInt32 = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun transformDiv(call: IrCall) =
|
||||||
|
irBinaryOp(call, intrinsics.jsDiv, toInt32 = BinaryOp(call).result.isInt())
|
||||||
|
|
||||||
|
private fun transformRem(call: IrCall) =
|
||||||
|
irBinaryOp(call, intrinsics.jsMod)
|
||||||
|
|
||||||
|
private fun transformIncrement(call: IrCall) =
|
||||||
|
transformCrement(call, intrinsics.jsPlus)
|
||||||
|
|
||||||
|
private fun transformDecrement(call: IrCall) =
|
||||||
|
transformCrement(call, intrinsics.jsMinus)
|
||||||
|
|
||||||
|
private fun transformCrement(call: IrCall, correspondingBinaryOp: IrFunction): IrExpression {
|
||||||
|
val operation = irCall(call, correspondingBinaryOp.symbol, dispatchReceiverAsFirstArgument = true).apply {
|
||||||
|
putValueArgument(1, buildInt(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertResultToPrimitiveType(operation, call.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun transformUnaryMinus(call: IrCall) =
|
||||||
|
convertResultToPrimitiveType(
|
||||||
|
irCall(call, intrinsics.jsUnaryMinus, dispatchReceiverAsFirstArgument = true),
|
||||||
|
call.type
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun convertResultToPrimitiveType(e: IrExpression, type: IrType) = when {
|
||||||
|
type.isInt() -> toInt32(e)
|
||||||
|
type.isByte() -> intrinsics.jsNumberToByte.call(e)
|
||||||
|
type.isShort() -> intrinsics.jsNumberToShort.call(e)
|
||||||
|
else -> e
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withLongCoercion(default: (IrCall) -> IrExpression): (IrCall) -> IrExpression = { call ->
|
||||||
assert(call.valueArgumentsCount == 1)
|
assert(call.valueArgumentsCount == 1)
|
||||||
val arg = call.getValueArgument(0)!!
|
val arg = call.getValueArgument(0)!!
|
||||||
|
|
||||||
@@ -211,8 +219,20 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo
|
|||||||
// LHS is Long => use as is
|
// LHS is Long => use as is
|
||||||
call
|
call
|
||||||
} else {
|
} else {
|
||||||
irCall(call, intrinsic.symbol, dispatchReceiverAsFirstArgument = true)
|
default(call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
fun IrFunctionSymbol.call(vararg arguments: IrExpression) =
|
||||||
|
JsIrBuilder.buildCall(this, owner.returnType).apply {
|
||||||
|
for ((idx, arg) in arguments.withIndex()) {
|
||||||
|
putValueArgument(idx, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toInt32(e: IrExpression) =
|
||||||
|
JsIrBuilder.buildCall(intrinsics.jsBitOr.symbol, irBuiltIns.intType).apply {
|
||||||
|
putValueArgument(0, e)
|
||||||
|
putValueArgument(1, buildInt(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -279,7 +279,7 @@ class StateMachineBuilder(
|
|||||||
if (expression is IrReturnableBlock) processReturnableBlock(expression) else super.visitBlock(expression)
|
if (expression is IrReturnableBlock) processReturnableBlock(expression) else super.visitBlock(expression)
|
||||||
|
|
||||||
private fun implicitCast(value: IrExpression, toType: IrType) =
|
private fun implicitCast(value: IrExpression, toType: IrType) =
|
||||||
JsIrBuilder.buildTypeOperator(toType, IrTypeOperator.IMPLICIT_CAST, value, toType, toType.classifierOrFail)
|
JsIrBuilder.buildImplicitCast(value, toType)
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall) {
|
override fun visitCall(expression: IrCall) {
|
||||||
super.visitCall(expression)
|
super.visitCall(expression)
|
||||||
|
|||||||
+7
@@ -140,4 +140,11 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
|
|||||||
// TODO check when w/o else branch and empty when
|
// TODO check when w/o else branch and empty when
|
||||||
return expression.toJsNode(this, context, ::JsConditional)!!
|
return expression.toJsNode(this, context, ::JsConditional)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: JsGenerationContext): JsExpression {
|
||||||
|
return when (expression.operator) {
|
||||||
|
IrTypeOperator.IMPLICIT_CAST -> expression.argument.accept(this, data)
|
||||||
|
else -> throw IllegalStateException("All type operator calls except IMPLICIT_CAST should be lowered at this point")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val i1: Int = Int.MAX_VALUE
|
val i1: Int = Int.MAX_VALUE
|
||||||
val i2 = i1 + 1
|
val i2 = i1 + 1
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val a: Long = 2147483647 + 1
|
val a: Long = 2147483647 + 1
|
||||||
if (a != -2147483648L) return "fail: in this case we should add to ints and than cast the result to long - overflow expected"
|
if (a != -2147483648L) return "fail: in this case we should add to ints and than cast the result to long - overflow expected"
|
||||||
|
|||||||
+5
@@ -6404,6 +6404,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/number/longUnaryOperations.kt");
|
runTest("js/js.translator/testData/box/number/longUnaryOperations.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("mixedTypesOverflow.kt")
|
||||||
|
public void testMixedTypesOverflow() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/number/mixedTypesOverflow.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("mulInt32.kt")
|
@TestMetadata("mulInt32.kt")
|
||||||
public void testMulInt32() throws Exception {
|
public void testMulInt32() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/number/mulInt32.kt");
|
runTest("js/js.translator/testData/box/number/mulInt32.kt");
|
||||||
|
|||||||
+5
@@ -6404,6 +6404,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/number/longUnaryOperations.kt");
|
runTest("js/js.translator/testData/box/number/longUnaryOperations.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("mixedTypesOverflow.kt")
|
||||||
|
public void testMixedTypesOverflow() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/number/mixedTypesOverflow.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("mulInt32.kt")
|
@TestMetadata("mulInt32.kt")
|
||||||
public void testMulInt32() throws Exception {
|
public void testMulInt32() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/number/mulInt32.kt");
|
runTest("js/js.translator/testData/box/number/mulInt32.kt");
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1282
|
// EXPECTED_REACHABLE_NODES: 1282
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1266
|
// EXPECTED_REACHABLE_NODES: 1266
|
||||||
|
|
||||||
// MODULE: lib1
|
// MODULE: lib1
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1293
|
// EXPECTED_REACHABLE_NODES: 1293
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1280
|
// EXPECTED_REACHABLE_NODES: 1280
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1282
|
// EXPECTED_REACHABLE_NODES: 1282
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1284
|
// EXPECTED_REACHABLE_NODES: 1284
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1281
|
// EXPECTED_REACHABLE_NODES: 1281
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// EXPECTED_REACHABLE_NODES: 1284
|
||||||
|
package foo
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val byteOne = 1.toByte()
|
||||||
|
val shortOne = 1.toShort()
|
||||||
|
var v: Int
|
||||||
|
|
||||||
|
v = maxInt() + byteOne
|
||||||
|
if (v != minInt()) return "fail1: $v"
|
||||||
|
|
||||||
|
v = minInt() - byteOne
|
||||||
|
if (v != maxInt()) return "fail2: $v"
|
||||||
|
|
||||||
|
v = maxInt() + shortOne
|
||||||
|
if (v != minInt()) return "fail3: $v"
|
||||||
|
|
||||||
|
v = minInt() - shortOne
|
||||||
|
if (v != maxInt()) return "fail4: $v"
|
||||||
|
|
||||||
|
v = maxInt() * Byte.MAX_VALUE
|
||||||
|
if (v != 2147483521) return "fail5: $v"
|
||||||
|
|
||||||
|
v = maxInt() * Short.MAX_VALUE
|
||||||
|
if (v != 2147450881) return "fail6: $v"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun minInt() = Int.MIN_VALUE
|
||||||
|
fun maxInt() = Int.MAX_VALUE
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1289
|
// EXPECTED_REACHABLE_NODES: 1289
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1289
|
// EXPECTED_REACHABLE_NODES: 1289
|
||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kotlin.js
|
||||||
|
|
||||||
|
// TODO: Polyfill
|
||||||
|
fun imul(a: dynamic, b: dynamic) =
|
||||||
|
js("((a & 0xffff0000) * (b & 0xffff) + (a & 0xffff) * (b | 0)) | 0")
|
||||||
Reference in New Issue
Block a user