From bad9534abd1506da1ad5c479fe8373c5df2b965d Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Thu, 4 Oct 2018 13:31:57 +0300 Subject: [PATCH] [JS IR BE] Support integer operation overflow --- .../kotlin/ir/backend/js/JsIntrinsics.kt | 2 + .../kotlin/ir/backend/js/ir/IrBuilder.kt | 15 ++ .../backend/js/lower/calls/CallsLowering.kt | 2 + .../calls/NumberConversionCallsTransformer.kt | 98 +++++++++ .../calls/NumberOperatorCallsTransformer.kt | 194 ++++++++++-------- .../lower/coroutines/StateMachineBuilder.kt | 2 +- .../IrElementToJsExpressionTransformer.kt | 7 + .../codegen/box/binaryOp/overflowInt.kt | 1 - .../codegen/box/binaryOp/overflowLong.kt | 1 - .../js/test/semantics/BoxJsTestGenerated.java | 5 + .../test/semantics/IrBoxJsTestGenerated.java | 5 + .../box/number/assignmentIntOverflow.kt | 1 - .../box/number/constantPropagation.kt | 1 - .../number/conversionsWithoutTruncation.kt | 1 - .../testData/box/number/division.kt | 1 - .../testData/box/number/intIncDecOverflow.kt | 1 - .../testData/box/number/intOverflow.kt | 1 - .../testData/box/number/kt2342.kt | 1 - .../testData/box/number/mixedTypesOverflow.kt | 31 +++ .../testData/box/number/numberCompareTo.kt | 1 - .../testData/box/number/numberConversions.kt | 1 - libraries/stdlib/js/irRuntime/misc.kt | 10 + 22 files changed, 283 insertions(+), 99 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt create mode 100644 js/js.translator/testData/box/number/mixedTypesOverflow.kt create mode 100644 libraries/stdlib/js/irRuntime/misc.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index 21300f472c9..439612e4b6f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -143,6 +143,8 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC val jsCompareTo = getInternalFunction("compareTo") val jsEquals = getInternalFunction("equals") + val jsImul = getInternalFunction("imul") + // Coroutines val jsCoroutineContext = context.symbolTable.referenceSimpleFunction(context.coroutineContextProperty.getter!!) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt index bca44a8d7b7..ea2c6519456 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt @@ -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.IrVariableSymbolImpl 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.name.Name import org.jetbrains.kotlin.types.Variance @@ -233,6 +234,10 @@ object JsIrBuilder { fun buildTypeOperator(type: IrType, operator: IrTypeOperator, argument: IrExpression, toType: IrType, symbol: IrClassifierSymbol) = 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 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) @@ -267,6 +272,16 @@ fun IrClass.simpleFunctions(): List = this.declarations.flatMa } // 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( call: IrCall, newSymbol: IrFunctionSymbol, diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt index 95c4bc607e9..04b99b12f72 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/CallsLowering.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.expressions.IrCall 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.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -19,6 +20,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid class CallsLowering(val context: JsIrBackendContext) : FileLoweringPass { private val transformers = listOf( NumberOperatorCallsTransformer(context), + NumberConversionCallsTransformer(context), DynamicCallsTransformer(context), EqualityAndComparisonCallsTransformer(context), PrimitiveContainerMemberCallTransformer(context), diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt new file mode 100644 index 00000000000..4f81d9f573e --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberConversionCallsTransformer.kt @@ -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) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt index 9105496a741..6a9c406aae1 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/calls/NumberOperatorCallsTransformer.kt @@ -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.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.IrSimpleFunction +import org.jetbrains.kotlin.ir.declarations.IrFunction 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.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol 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 class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransformer { private val intrinsics = context.intrinsics private val irBuiltIns = context.irBuiltIns - private val primitiveNumbers = - irBuiltIns.run { listOf(intType, shortType, byteType, floatType, doubleType) } + private fun buildInt(v: Int) = JsIrBuilder.buildInt(irBuiltIns.intType, v) private val memberToTransformer = MemberToTransformer().apply { + + val primitiveNumbers = + irBuiltIns.run { listOf(intType, shortType, byteType, floatType, doubleType) } + for (type in primitiveNumbers) { 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) @@ -52,87 +52,23 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo 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) { add(type, Name.identifier("rangeTo"), ::transformRangeTo) } for (type in primitiveNumbers) { - // TODO: use increment and decrement when it's possible - add(type, OperatorNames.INC) { - 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)) - } - } + add(type, OperatorNames.INC, ::transformIncrement) + add(type, OperatorNames.DEC, ::transformDecrement) } for (type in primitiveNumbers) { - add(type, OperatorNames.ADD, withLongCoercion(intrinsics.jsPlus)) - add(type, OperatorNames.SUB, withLongCoercion(intrinsics.jsMinus)) - add(type, OperatorNames.MUL, withLongCoercion(intrinsics.jsMult)) - add(type, OperatorNames.DIV, withLongCoercion(intrinsics.jsDiv)) - add(type, OperatorNames.MOD, withLongCoercion(intrinsics.jsMod)) - add(type, OperatorNames.REM, withLongCoercion(intrinsics.jsMod)) + add(type, OperatorNames.ADD, withLongCoercion(::transformAdd)) + add(type, OperatorNames.SUB, withLongCoercion(::transformSub)) + add(type, OperatorNames.MUL, withLongCoercion(::transformMul)) + add(type, OperatorNames.DIV, withLongCoercion(::transformDiv)) + add(type, OperatorNames.MOD, withLongCoercion(::transformRem)) + 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 { @@ -146,10 +82,6 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo return call } - private fun useDispatchReceiver(call: IrCall): IrExpression { - return call.dispatchReceiver!! - } - private fun transformRangeTo(call: IrCall): IrExpression { if (call.valueArgumentsCount != 1) return call 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) val arg = call.getValueArgument(0)!! @@ -211,8 +219,20 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo // LHS is Long => use as is call } 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)) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt index b209f5db28a..bebe4ebddd5 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt @@ -279,7 +279,7 @@ class StateMachineBuilder( if (expression is IrReturnableBlock) processReturnableBlock(expression) else super.visitBlock(expression) 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) { super.visitCall(expression) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index 39940c49bcc..6eb58d646a6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -140,4 +140,11 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer expression.argument.accept(this, data) + else -> throw IllegalStateException("All type operator calls except IMPLICIT_CAST should be lowered at this point") + } + } } \ No newline at end of file diff --git a/compiler/testData/codegen/box/binaryOp/overflowInt.kt b/compiler/testData/codegen/box/binaryOp/overflowInt.kt index a0504afe7ab..4d5b899987f 100644 --- a/compiler/testData/codegen/box/binaryOp/overflowInt.kt +++ b/compiler/testData/codegen/box/binaryOp/overflowInt.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun box(): String { val i1: Int = Int.MAX_VALUE val i2 = i1 + 1 diff --git a/compiler/testData/codegen/box/binaryOp/overflowLong.kt b/compiler/testData/codegen/box/binaryOp/overflowLong.kt index 946518a1495..28d37ab1c12 100644 --- a/compiler/testData/codegen/box/binaryOp/overflowLong.kt +++ b/compiler/testData/codegen/box/binaryOp/overflowLong.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR fun box(): String { 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" diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 185eb2852d9..00fe137d9ef 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -6404,6 +6404,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { 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") public void testMulInt32() throws Exception { runTest("js/js.translator/testData/box/number/mulInt32.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java index aea191f2ab9..c00fcba1d2b 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrBoxJsTestGenerated.java @@ -6404,6 +6404,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { 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") public void testMulInt32() throws Exception { runTest("js/js.translator/testData/box/number/mulInt32.kt"); diff --git a/js/js.translator/testData/box/number/assignmentIntOverflow.kt b/js/js.translator/testData/box/number/assignmentIntOverflow.kt index 6520fd5e7a5..e281aee9885 100644 --- a/js/js.translator/testData/box/number/assignmentIntOverflow.kt +++ b/js/js.translator/testData/box/number/assignmentIntOverflow.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1282 package foo diff --git a/js/js.translator/testData/box/number/constantPropagation.kt b/js/js.translator/testData/box/number/constantPropagation.kt index d2a4ab9eade..0a6e6b6b356 100644 --- a/js/js.translator/testData/box/number/constantPropagation.kt +++ b/js/js.translator/testData/box/number/constantPropagation.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1266 // MODULE: lib1 diff --git a/js/js.translator/testData/box/number/conversionsWithoutTruncation.kt b/js/js.translator/testData/box/number/conversionsWithoutTruncation.kt index d154b903327..8e20fed55b2 100644 --- a/js/js.translator/testData/box/number/conversionsWithoutTruncation.kt +++ b/js/js.translator/testData/box/number/conversionsWithoutTruncation.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1293 package foo diff --git a/js/js.translator/testData/box/number/division.kt b/js/js.translator/testData/box/number/division.kt index b49a61c7e14..0073e310517 100644 --- a/js/js.translator/testData/box/number/division.kt +++ b/js/js.translator/testData/box/number/division.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1280 package foo diff --git a/js/js.translator/testData/box/number/intIncDecOverflow.kt b/js/js.translator/testData/box/number/intIncDecOverflow.kt index 69ff9eec605..0b4a280de4b 100644 --- a/js/js.translator/testData/box/number/intIncDecOverflow.kt +++ b/js/js.translator/testData/box/number/intIncDecOverflow.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1282 package foo diff --git a/js/js.translator/testData/box/number/intOverflow.kt b/js/js.translator/testData/box/number/intOverflow.kt index 17503b071e1..88c92c32af0 100644 --- a/js/js.translator/testData/box/number/intOverflow.kt +++ b/js/js.translator/testData/box/number/intOverflow.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1284 package foo diff --git a/js/js.translator/testData/box/number/kt2342.kt b/js/js.translator/testData/box/number/kt2342.kt index 5962a448de2..28e1df7f8a7 100644 --- a/js/js.translator/testData/box/number/kt2342.kt +++ b/js/js.translator/testData/box/number/kt2342.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1281 package foo diff --git a/js/js.translator/testData/box/number/mixedTypesOverflow.kt b/js/js.translator/testData/box/number/mixedTypesOverflow.kt new file mode 100644 index 00000000000..c4f15978fe2 --- /dev/null +++ b/js/js.translator/testData/box/number/mixedTypesOverflow.kt @@ -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 \ No newline at end of file diff --git a/js/js.translator/testData/box/number/numberCompareTo.kt b/js/js.translator/testData/box/number/numberCompareTo.kt index 01f2fa36889..f174edfa14b 100644 --- a/js/js.translator/testData/box/number/numberCompareTo.kt +++ b/js/js.translator/testData/box/number/numberCompareTo.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1289 package foo diff --git a/js/js.translator/testData/box/number/numberConversions.kt b/js/js.translator/testData/box/number/numberConversions.kt index ad3b33825aa..faa7565eb81 100644 --- a/js/js.translator/testData/box/number/numberConversions.kt +++ b/js/js.translator/testData/box/number/numberConversions.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1289 package foo diff --git a/libraries/stdlib/js/irRuntime/misc.kt b/libraries/stdlib/js/irRuntime/misc.kt new file mode 100644 index 00000000000..e91effd202a --- /dev/null +++ b/libraries/stdlib/js/irRuntime/misc.kt @@ -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") \ No newline at end of file