JS backend: fix KT-5738 JS backend: result of toByte() should be in the range of bytes

#KT-5738 Fixed
This commit is contained in:
Michael Nedzelsky
2014-09-06 15:40:49 +04:00
parent a632e9e21d
commit cc4f84ffae
7 changed files with 168 additions and 34 deletions
@@ -69,4 +69,12 @@ public final class NumberTest extends SingleFileTranslationTest {
public void testNumberCompareTo() throws Exception {
checkFooBoxIsOk();
}
public void testConversionsWithoutTruncation() throws Exception {
checkFooBoxIsOk();
}
public void testConversionsWithTruncation() throws Exception {
checkFooBoxIsOk();
}
}
@@ -16,47 +16,32 @@
package org.jetbrains.k2js.translate.intrinsic.functions.factories;
import com.google.common.collect.Sets;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.intrinsic.functions.basic.FunctionIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import java.util.List;
import java.util.Set;
import static org.jetbrains.jet.lang.types.expressions.OperatorConventions.*;
import static org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder.pattern;
//TODO: support longs and chars
public final class NumberConversionFIF extends CompositeFIF {
@NotNull
private static final NamePredicate SUPPORTED_CONVERSIONS;
static {
Set<Name> supportedConversions = Sets.newHashSet(NUMBER_CONVERSIONS);
//TODO: support longs and chars
supportedConversions.remove(CHAR);
supportedConversions.remove(LONG);
SUPPORTED_CONVERSIONS = new NamePredicate(supportedConversions);
}
@NotNull
private static final NamePredicate FLOATING_POINT_CONVERSIONS = new NamePredicate(FLOAT, DOUBLE);
@NotNull
private static final NamePredicate INTEGER_CONVERSIONS = new NamePredicate(INT, SHORT, BYTE);
@NotNull
private static final FunctionIntrinsic RETURN_RECEIVER = new FunctionIntrinsic() {
@NotNull
@Override
public JsExpression apply(@Nullable JsExpression receiver,
public JsExpression apply(
@Nullable JsExpression receiver,
@NotNull List<JsExpression> arguments,
@NotNull TranslationContext context) {
@NotNull TranslationContext context
) {
assert receiver != null;
assert arguments.isEmpty();
return receiver;
@@ -65,27 +50,68 @@ public final class NumberConversionFIF extends CompositeFIF {
@NotNull
public static final String INTEGER_NUMBER_TYPES = "Int|Byte|Short";
//NOTE: treat Number as if it is floating point type
@NotNull
private static final String FLOATING_POINT_NUMBER_TYPES = "Float|Double|Number";
@NotNull
private static final FunctionIntrinsic GET_INTEGER_PART = new FunctionIntrinsic() {
private static final FunctionIntrinsic TO_INT32 = new FunctionIntrinsic() {
@NotNull
@Override
public JsExpression apply(@Nullable JsExpression receiver,
public JsExpression apply(
@Nullable JsExpression receiver,
@NotNull List<JsExpression> arguments,
@NotNull TranslationContext context) {
@NotNull TranslationContext context
) {
assert receiver != null;
assert arguments.isEmpty();
return JsAstUtils.toInt32(receiver, context);
}
};
@NotNull
private static final FunctionIntrinsic TO_SHORT = new FunctionIntrinsic() {
@NotNull
@Override
public JsExpression apply(
@Nullable JsExpression receiver,
@NotNull List<JsExpression> arguments,
@NotNull TranslationContext context
) {
assert receiver != null;
assert arguments.isEmpty();
return JsAstUtils.toShort(receiver);
}
};
@NotNull
private static final FunctionIntrinsic TO_BYTE = new FunctionIntrinsic() {
@NotNull
@Override
public JsExpression apply(
@Nullable JsExpression receiver,
@NotNull List<JsExpression> arguments,
@NotNull TranslationContext context
) {
assert receiver != null;
assert arguments.isEmpty();
return JsAstUtils.toByte(receiver);
}
};
@NotNull
private static final Predicate<FunctionDescriptor> returnReceiverPredicate = Predicates.or(
pattern("Int.toInt|toFloat|toDouble"),
pattern("Short.toShort|toInt|toFloat|toDouble"),
pattern("Byte.toByte|toShort|toInt|toFloat|toDouble"),
pattern("Float|Double|Number.toFloat|toDouble")
);
@NotNull
public static final FunctionIntrinsicFactory INSTANCE = new NumberConversionFIF();
//NOTE: treat Number as if it is floating point type
private NumberConversionFIF() {
add(pattern(INTEGER_NUMBER_TYPES, SUPPORTED_CONVERSIONS), RETURN_RECEIVER);
add(pattern(FLOATING_POINT_NUMBER_TYPES, INTEGER_CONVERSIONS), GET_INTEGER_PART);
add(pattern(FLOATING_POINT_NUMBER_TYPES, FLOATING_POINT_CONVERSIONS), RETURN_RECEIVER);
add(returnReceiverPredicate, RETURN_RECEIVER);
add(pattern("Float|Double|Number.toInt"), TO_INT32);
add(pattern("Int|Float|Double|Number.toShort"), TO_SHORT);
add(pattern("Short|Int|Float|Double|Number.toByte"), TO_BYTE);
}
}
}
@@ -87,7 +87,7 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
) {
assert receiver != null;
assert arguments.size() == 1;
return JsAstUtils.compareTo(receiver, arguments.get(0), context);
return JsAstUtils.compareTo(receiver, arguments.get(0));
}
};
@@ -123,6 +123,16 @@ public final class JsAstUtils {
return new JsBinaryOperation(JsBinaryOperator.BIT_OR, expression, context.program().getNumberLiteral(0));
}
@NotNull
public static JsExpression toShort(@NotNull JsExpression expression) {
return new JsInvocation(new JsNameRef(OperatorConventions.SHORT.getIdentifier(), Namer.KOTLIN_OBJECT_REF), expression);
}
@NotNull
public static JsExpression toByte(@NotNull JsExpression expression) {
return new JsInvocation(new JsNameRef(OperatorConventions.BYTE.getIdentifier(), Namer.KOTLIN_OBJECT_REF), expression);
}
@NotNull
public static JsExpression compareTo(@NotNull JsExpression left, @NotNull JsExpression right) {
return new JsInvocation(new JsNameRef(OperatorConventions.COMPARE_TO.getIdentifier(), Namer.KOTLIN_OBJECT_REF), left, right);
+8
View File
@@ -89,6 +89,14 @@
return a < b ? -1 : a > b ? 1 : 0;
};
Kotlin.toShort = function(a) {
return (a & 0xFFFF) << 16 >> 16;
};
Kotlin.toByte = function(a) {
return (a & 0xFF) << 24 >> 24;
};
Kotlin.intUpto = function (from, to) {
return new Kotlin.NumberRange(from, to);
};
@@ -0,0 +1,27 @@
package foo
fun box(): String {
assertEquals(65, 321.0.toByte())
assertEquals(-56, 200.0.toByte())
assertEquals(65, 321.0f.toByte())
assertEquals(-56, 200.0f.toByte())
assertEquals(65, 321.toByte())
assertEquals(-56, 200.toByte())
assertEquals(65, (321: Short).toByte())
assertEquals(-56, (200: Short).toByte())
assertEquals(-1, 65535.0.toShort())
assertEquals(-1, 65535.0f.toShort())
assertEquals(-1, 65535.toShort())
assertEquals(65535, 65535.2.toInt())
assertEquals(23, 23.6f.toInt())
assertEquals(-12, -12.4.toShort())
assertEquals(-12, -12.4.toByte())
return "OK"
}
@@ -0,0 +1,55 @@
package foo
fun testForNumber(numberX: Number) {
assertEquals(true, 65.0 == numberX.toDouble())
assertEquals(true, 65.0f == numberX.toFloat())
assertEquals(true, 65 == numberX.toInt())
assertEquals(true, (65: Short) == numberX.toShort())
assertEquals(true, (65: Byte) == numberX.toByte())
}
fun box(): String {
testForNumber(65.0)
testForNumber(65.0f)
testForNumber(65)
testForNumber(65: Short)
testForNumber(65: Byte)
var doubleX: Double = 65.0
assertEquals(true, 65.0 == doubleX.toDouble())
assertEquals(true, 65.0f == doubleX.toFloat())
assertEquals(true, 65 == doubleX.toInt())
assertEquals(true, (65: Short) == doubleX.toShort())
assertEquals(true, (65: Byte) == doubleX.toByte())
var floatX: Float = 65.0f
assertEquals(true, 65.0 == floatX.toDouble())
assertEquals(true, 65.0f == floatX.toFloat())
assertEquals(true, 65 == floatX.toInt())
assertEquals(true, (65: Short) == floatX.toShort())
assertEquals(true, (65: Byte) == floatX.toByte())
val intX: Int = 65
assertEquals(true, 65.0 == intX.toDouble())
assertEquals(true, 65.0f == intX.toFloat())
assertEquals(true, 65 == intX.toInt())
assertEquals(true, (65: Short) == intX.toShort())
assertEquals(true, (65: Byte) == intX.toByte())
val shortX: Short = 65: Short
assertEquals(true, 65.0 == shortX.toDouble())
assertEquals(true, 65.0f == shortX.toFloat())
assertEquals(true, 65 == shortX.toInt())
assertEquals(true, (65: Short) == shortX.toShort())
assertEquals(true, (65: Byte) == shortX.toByte())
val byteX: Byte = 65: Byte
assertEquals(true, 65.0 == byteX.toDouble())
assertEquals(true, 65.0f == byteX.toFloat())
assertEquals(true, 65 == byteX.toInt())
assertEquals(true, (65: Short) == byteX.toShort())
assertEquals(true, (65: Byte) == byteX.toByte())
return "OK"
}