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 35ab34c3cdc..ac8a38a2777 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 @@ -41,7 +41,8 @@ class NumberOperatorCallsTransformer(context: JsIrBackendContext) : CallsTransfo irBuiltIns.intType.let { add(it, OperatorNames.SHL, intrinsics.jsBitShiftL) add(it, OperatorNames.SHR, intrinsics.jsBitShiftR) - add(it, OperatorNames.SHRU, intrinsics.jsBitShiftRU) + // shifting of a negative int to 0 bytes returns the unsigned int, therefore we have to cast it back to the signed int + add(it, OperatorNames.SHRU) { call -> irBinaryOp(call, intrinsics.jsBitShiftRU, toInt32 = true) } add(it, OperatorNames.AND, intrinsics.jsBitAnd) add(it, OperatorNames.OR, intrinsics.jsBitOr) add(it, OperatorNames.XOR, intrinsics.jsBitXor) diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index eb900823fe9..47023e69722 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -8070,6 +8070,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/number/incDecOptimization.kt"); } + @Test + @TestMetadata("intBitOperations.kt") + public void testIntBitOperations() throws Exception { + runTest("js/js.translator/testData/box/number/intBitOperations.kt"); + } + @Test @TestMetadata("intConversions.kt") public void testIntConversions() throws Exception { diff --git a/js/js.translator/testData/box/number/intBitOperations.kt b/js/js.translator/testData/box/number/intBitOperations.kt new file mode 100644 index 00000000000..10cc9af033f --- /dev/null +++ b/js/js.translator/testData/box/number/intBitOperations.kt @@ -0,0 +1,69 @@ +// DONT_TARGET_EXACT_BACKEND: JS + +fun testShl() { + // wrapper prevents the constants folding + infix fun Int.myShl(y: Int): Int = this shl y + + assertEquals(0 myShl 0, 0) + assertEquals(0 myShl 1, 0) + assertEquals(1 myShl 1, 2) + assertEquals(3 myShl 1, 6) + + assertEquals(-1 myShl 0, -1) + assertEquals(-1 myShl 1, -2) + assertEquals(-1 myShl 2, -4) + + assertEquals(Int.MIN_VALUE myShl 0, Int.MIN_VALUE) + assertEquals(Int.MIN_VALUE myShl 1, 0) + + assertEquals(Int.MAX_VALUE myShl 0, Int.MAX_VALUE) + assertEquals(Int.MAX_VALUE myShl 1, -2) +} + +fun testShr() { + // wrapper prevents the constants folding + infix fun Int.myShr(y: Int): Int = this shr y + + assertEquals(0 myShr 0, 0) + assertEquals(0 myShr 1, 0) + assertEquals(1 myShr 1, 0) + assertEquals(3 myShr 1, 1) + + assertEquals(-1 myShr 0, -1) + assertEquals(-1 myShr 1, -1) + assertEquals(-1 myShr 2, -1) + + assertEquals(Int.MIN_VALUE myShr 0, Int.MIN_VALUE) + assertEquals(Int.MIN_VALUE myShr 1, Int.MIN_VALUE / 2) + + assertEquals(Int.MAX_VALUE myShr 0, Int.MAX_VALUE) + assertEquals(Int.MAX_VALUE myShr 1, 0x3FFFFFFF) +} + +fun testUshr() { + // wrapper prevents the constants folding + infix fun Int.myUshr(y: Int): Int = this ushr y + + assertEquals(0 myUshr 0, 0) + assertEquals(0 myUshr 1, 0) + assertEquals(1 myUshr 1, 0) + assertEquals(3 myUshr 1, 1) + + assertEquals(-1 myUshr 0, -1) + assertEquals(-1 myUshr 1, 0x7FFFFFFF) + assertEquals(-1 myUshr 2, 0x3FFFFFFF) + + assertEquals(Int.MIN_VALUE myUshr 0, Int.MIN_VALUE) + assertEquals(Int.MIN_VALUE myUshr 1, 0x40000000) + + assertEquals(Int.MAX_VALUE myUshr 0, Int.MAX_VALUE) + assertEquals(Int.MAX_VALUE myUshr 1, 0x3FFFFFFF) +} + +fun box(): String { + testShl() + testShr() + testUshr() + + return "OK" +}