JS: provide overflow behaviour for int binary operations (see KT-7733)
This commit is contained in:
@@ -5999,6 +5999,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("intOverflow.kt")
|
||||
public void testIntOverflow() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/number/intOverflow.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kt2342.kt")
|
||||
public void testKt2342() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/number/kt2342.kt");
|
||||
|
||||
@@ -465,6 +465,10 @@ public final class Namer {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static JsNameRef imul() {
|
||||
return pureFqn("imul", kotlinObject());
|
||||
}
|
||||
|
||||
public static boolean requiresEscaping(@NotNull String name) {
|
||||
// TODO: remove if there is existing implementation of this method
|
||||
// TODO: handle JavaScript keywords
|
||||
|
||||
+29
-8
@@ -22,12 +22,14 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
|
||||
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
|
||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
||||
import com.google.dart.compiler.backend.js.ast.JsInvocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.kotlin.js.patterns.DescriptorPredicate;
|
||||
import org.jetbrains.kotlin.js.patterns.NamePredicate;
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer;
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.RangeToIntrinsic;
|
||||
@@ -61,13 +63,13 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static final BinaryOperationIntrinsicBase INTEGER_DIVISION_INTRINSIC = new BinaryOperationIntrinsicBase() {
|
||||
private static final BinaryOperationIntrinsicBase INT_MULTIPLICATION_INTRINSIC = new BinaryOperationIntrinsicBase() {
|
||||
@NotNull
|
||||
@Override
|
||||
public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
|
||||
JsBinaryOperation div = new JsBinaryOperation(JsBinaryOperator.DIV, left, right);
|
||||
return JsAstUtils.toInt32(div);
|
||||
public JsExpression doApply(
|
||||
@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context
|
||||
) {
|
||||
return new JsInvocation(Namer.imul(), left, right);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,6 +93,10 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
|
||||
@NotNull
|
||||
private static final NamePredicate BINARY_OPERATIONS = new NamePredicate(OperatorNameConventions.BINARY_OPERATION_NAMES);
|
||||
|
||||
private static final DescriptorPredicate INT_BINARY_OPERATIONS = pattern("Int.plus|minus|div(Int)");
|
||||
private static final DescriptorPredicate SIMPLE_INT_MULTIPLICATION = pattern("Byte|Short.times(Byte|Short)");
|
||||
private static final DescriptorPredicate INT_DIVISION = pattern("Byte|Short|Int.div(Byte|Short|Int)");
|
||||
private static final DescriptorPredicate PRIMITIVE_NUMBERS_BINARY_OPERATIONS =
|
||||
pattern(NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS, BINARY_OPERATIONS);
|
||||
|
||||
@@ -102,6 +108,7 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
);
|
||||
private static final DescriptorPredicate BOOLEAN_OPERATIONS = pattern("Boolean.or|and|xor");
|
||||
private static final DescriptorPredicate STRING_PLUS = pattern("String.plus");
|
||||
private static final DescriptorPredicate INT_MULTIPLICATION = pattern("Int.times(Int)");
|
||||
|
||||
private static final DescriptorPredicate CHAR_RANGE_TO = pattern("Char.rangeTo(Char)");
|
||||
private static final DescriptorPredicate NUMBER_RANGE_TO = pattern("Byte|Short|Int.rangeTo(Byte|Short|Int)");
|
||||
@@ -139,9 +146,8 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (pattern("Int|Short|Byte.div(Int|Short|Byte)").apply(descriptor)) {
|
||||
return INTEGER_DIVISION_INTRINSIC;
|
||||
if (INT_MULTIPLICATION.apply(descriptor)) {
|
||||
return INT_MULTIPLICATION_INTRINSIC;
|
||||
}
|
||||
if (NUMBER_RANGE_TO.apply(descriptor)) {
|
||||
return new RangeToIntrinsic(descriptor);
|
||||
@@ -153,6 +159,9 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
}
|
||||
}
|
||||
JsBinaryOperator operator = getOperator(descriptor);
|
||||
if (INT_BINARY_OPERATIONS.apply(descriptor) || SIMPLE_INT_MULTIPLICATION.apply(descriptor) || INT_DIVISION.apply(descriptor)) {
|
||||
return new IntBinaryOperationFunctionIntrinsic(operator);
|
||||
}
|
||||
BinaryOperationIntrinsicBase result = new PrimitiveBinaryOperationFunctionIntrinsic(operator);
|
||||
|
||||
if (pattern("Char.plus|minus(Int)").apply(descriptor)) {
|
||||
@@ -196,6 +205,18 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private static class IntBinaryOperationFunctionIntrinsic extends PrimitiveBinaryOperationFunctionIntrinsic {
|
||||
private IntBinaryOperationFunctionIntrinsic(@NotNull JsBinaryOperator operator) {
|
||||
super(operator);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
|
||||
return JsAstUtils.toInt32(super.doApply(left, right, context));
|
||||
}
|
||||
}
|
||||
|
||||
private static class CharAndIntBinaryOperationFunctionIntrinsic extends BinaryOperationIntrinsicBase {
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package foo
|
||||
|
||||
// CHECK_CONTAINS_NO_CALLS: multiplyInline
|
||||
// CHECK_CONTAINS_NO_CALLS: multiplyInline except=imul
|
||||
// CHECK_NOT_CALLED: runNoinline
|
||||
|
||||
internal inline fun multiply(a: Int, b: Int) = a * b
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package foo
|
||||
|
||||
// CHECK_CONTAINS_NO_CALLS: squareMultipliedByTwo
|
||||
// CHECK_CONTAINS_NO_CALLS: squareMultipliedByTwo except=imul
|
||||
|
||||
internal inline fun inline1(a: Int): Int {
|
||||
return a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package foo
|
||||
|
||||
// CHECK_CONTAINS_NO_CALLS: squareMultipliedByTwo
|
||||
// CHECK_CONTAINS_NO_CALLS: squareMultipliedByTwo except=imul
|
||||
|
||||
internal inline fun inline1(a: Int): Int {
|
||||
return a
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package foo
|
||||
|
||||
// CHECK_CONTAINS_NO_CALLS: multiplyNoInline
|
||||
// CHECK_CONTAINS_NO_CALLS: multiplyNoInline except=imul
|
||||
|
||||
internal inline fun multiply(a: Int, b: Int): Int {
|
||||
return a * b
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package foo
|
||||
|
||||
// CHECK_CONTAINS_NO_CALLS: maxBySquare
|
||||
// CHECK_CONTAINS_NO_CALLS: maxBySquare except=imul
|
||||
|
||||
internal data class Result(var value: Int = 0, var invocationCount: Int = 0)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package foo
|
||||
|
||||
// CHECK_CONTAINS_NO_CALLS: test
|
||||
// CHECK_CONTAINS_NO_CALLS: test except=imul
|
||||
// CHECK_VARS_COUNT: function=test count=0
|
||||
|
||||
// A copy of stdlib run function.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package foo
|
||||
|
||||
fun bigValue() = 0x7FFFFFFC
|
||||
|
||||
fun mediumValue() = 0x12345
|
||||
|
||||
fun box(): String {
|
||||
var v = bigValue() + 1
|
||||
if (v != 0x7FFFFFFD) return "fail1: $v"
|
||||
|
||||
v = bigValue() + 8
|
||||
if (v != -0x7FFFFFFC) return "fail2: $v"
|
||||
|
||||
v = mediumValue() * 0x23456
|
||||
if (v != -2112496338) return "fail3: $v"
|
||||
|
||||
v = bigValue() * bigValue()
|
||||
if (v != 16) return "fail4: $v"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -313,4 +313,11 @@
|
||||
Kotlin.kotlinModuleMetadata = function (abiVersion, moduleName, data) {
|
||||
};
|
||||
|
||||
Kotlin.imul = Math.imul || function (a, b) {
|
||||
var ah = (a >>> 16) & 0xffff;
|
||||
var al = a & 0xffff;
|
||||
var bh = (b >>> 16) & 0xffff;
|
||||
var bl = b & 0xffff;
|
||||
return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0);
|
||||
};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user