From 6aff9803c4cccd9f61750b2f853cdd1cbb006709 Mon Sep 17 00:00:00 2001 From: Michael Nedzelsky Date: Wed, 24 Sep 2014 18:24:59 +0400 Subject: [PATCH] JS backend: support for char type --- .../k2js/translate/context/Namer.java | 2 + .../translate/context/StandardClasses.java | 6 ++ .../expression/PatternTranslator.java | 3 + .../functions/FunctionIntrinsics.java | 2 +- .../functions/factories/LongOperationFIF.kt | 6 ++ ...onFIF.kt => NumberAndCharConversionFIF.kt} | 14 +++- .../PrimitiveBinaryOperationFIF.java | 70 ++++++++++++++++- .../factories/PrimitiveUnaryOperationFIF.java | 77 +++++++++++++++++++ .../functions/patterns/NamePredicate.java | 3 + .../intrinsic/operation/CompareToBOIF.kt | 20 +++++ .../intrinsic/operation/LongCompareToBOIF.kt | 6 ++ .../k2js/translate/utils/JsAstUtils.java | 20 +++++ .../translate/utils/TranslationUtils.java | 3 +- js/js.translator/testData/kotlin_lib.js | 75 +++++++++++++++++- 14 files changed, 298 insertions(+), 9 deletions(-) rename js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/{NumberConversionFIF.kt => NumberAndCharConversionFIF.kt} (81%) diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java index 05c1cbfb614..3eaa53c145d 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/Namer.java @@ -38,10 +38,12 @@ public final class Namer { public static final String EQUALS_METHOD_NAME = getStableMangledNameForDescriptor(KotlinBuiltIns.getInstance().getAny(), "equals"); public static final String COMPARE_TO_METHOD_NAME = getStableMangledNameForDescriptor(KotlinBuiltIns.getInstance().getComparable(), "compareTo"); public static final String NUMBER_RANGE = "NumberRange"; + public static final String CHAR_RANGE = "CharRange"; public static final String LONG_FROM_NUMBER = "fromNumber"; public static final String LONG_TO_NUMBER = "toNumber"; public static final String LONG_FROM_INT = "fromInt"; public static final String PRIMITIVE_COMPARE_TO = "primitiveCompareTo"; + public static final String IS_CHAR = "isChar"; public static final String IS_NUMBER = "isNumber"; public static final String CALLEE_NAME = "$fun"; diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/context/StandardClasses.java b/js/js.translator/src/org/jetbrains/k2js/translate/context/StandardClasses.java index 277960da8af..043ece3afe5 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/context/StandardClasses.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/context/StandardClasses.java @@ -101,12 +101,18 @@ public final class StandardClasses { standardClasses.declare().forFQ("kotlin.LongRange").kotlinClass("LongRange") .methods("iterator", "contains").properties("start", "end", "increment"); + standardClasses.declare().forFQ("kotlin.CharRange").kotlinClass("CharRange") + .methods("iterator", "contains").properties("start", "end", "increment"); + standardClasses.declare().forFQ("kotlin.IntProgression").kotlinClass("NumberProgression") .methods("iterator", "contains").properties("start", "end", "increment"); standardClasses.declare().forFQ("kotlin.LongProgression").kotlinClass("LongProgression") .methods("iterator", "contains").properties("start", "end", "increment"); + standardClasses.declare().forFQ("kotlin.CharProgression").kotlinClass("CharProgression") + .methods("iterator", "contains").properties("start", "end", "increment"); + standardClasses.declare().forFQ("kotlin.Enum").kotlinClass("Enum"); standardClasses.declare().forFQ("kotlin.Comparable").kotlinClass("Comparable"); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/PatternTranslator.java b/js/js.translator/src/org/jetbrains/k2js/translate/expression/PatternTranslator.java index 9f38baebbad..bc30b6228cf 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/PatternTranslator.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/PatternTranslator.java @@ -99,6 +99,9 @@ public final class PatternTranslator extends AbstractTranslator { else if (NamePredicate.NUMBER.apply(typeName)) { return JsAstUtils.isNumber(expressionToMatch); } + else if (NamePredicate.CHAR.apply(typeName)) { + return JsAstUtils.isChar(expressionToMatch); + } else if (NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS.apply(typeName)) { jsSTypeName = "number"; } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/FunctionIntrinsics.java b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/FunctionIntrinsics.java index a0705a28c69..78f42f63a53 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/FunctionIntrinsics.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/FunctionIntrinsics.java @@ -46,7 +46,7 @@ public final class FunctionIntrinsics { register(StringOperationFIF.INSTANCE); register(ArrayFIF.INSTANCE); register(TopLevelFIF.INSTANCE); - register(NumberConversionFIF.INSTANCE$); + register(NumberAndCharConversionFIF.INSTANCE$); } private void register(@NotNull FunctionIntrinsicFactory instance) { diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/LongOperationFIF.kt b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/LongOperationFIF.kt index c7b0cbb5938..2669de43274 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/LongOperationFIF.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/LongOperationFIF.kt @@ -34,6 +34,8 @@ public object LongOperationFIF : FunctionIntrinsicFactory { val LONG_BINARY_OPERATION_INTEGER = pattern("Long.compareTo|rangeTo|plus|minus|times|div|mod(Int|Short|Byte)") val LONG_BINARY_OPERATION_FLOATING_POINT = pattern("Long.compareTo|rangeTo|plus|minus|times|div|mod(Double|Float)") val INTEGER_BINARY_OPERATION_LONG = pattern("Int|Short|Byte.compareTo|rangeTo|plus|minus|times|div|mod(Long)") + val CHAR_BINARY_OPERATION_LONG = pattern("Char.compareTo|rangeTo|plus|minus|times|div|mod(Long)") + val LONG_BINARY_OPERATION_CHAR = pattern("Long.compareTo|rangeTo|plus|minus|times|div|mod(Char)") val FLOATING_POINT_BINARY_OPERATION_LONG = pattern("Double|Float.compareTo|rangeTo|plus|minus|times|div|mod(Long)") private val longBinaryIntrinsics = @@ -89,6 +91,10 @@ public object LongOperationFIF : FunctionIntrinsicFactory { wrapIntrinsicIfPresent(longBinaryIntrinsics[operationName], { longFromInt(it) }, ID) LONG_BINARY_OPERATION_INTEGER.apply(descriptor) -> wrapIntrinsicIfPresent(longBinaryIntrinsics[operationName], ID, { longFromInt(it) }) + CHAR_BINARY_OPERATION_LONG.apply(descriptor) -> + wrapIntrinsicIfPresent(longBinaryIntrinsics[operationName], { longFromInt(charToInt(it)) }, ID) + LONG_BINARY_OPERATION_CHAR.apply(descriptor) -> + wrapIntrinsicIfPresent(longBinaryIntrinsics[operationName], ID, { longFromInt(charToInt(it)) }) FLOATING_POINT_BINARY_OPERATION_LONG.apply(descriptor) -> wrapIntrinsicIfPresent(floatBinaryIntrinsics[operationName], ID, { invokeMethod(it, Namer.LONG_TO_NUMBER) }) LONG_BINARY_OPERATION_FLOATING_POINT.apply(descriptor) -> diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/NumberConversionFIF.kt b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/NumberAndCharConversionFIF.kt similarity index 81% rename from js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/NumberConversionFIF.kt rename to js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/NumberAndCharConversionFIF.kt index 837e240dae9..eab6a3e9e01 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/NumberConversionFIF.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/NumberAndCharConversionFIF.kt @@ -24,7 +24,7 @@ import org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder. import org.jetbrains.k2js.translate.utils.ID import org.jetbrains.k2js.translate.utils.JsAstUtils.* -public object NumberConversionFIF : CompositeFIF() { +public object NumberAndCharConversionFIF : CompositeFIF() { val USE_AS_IS = Predicates.or( pattern("Int.toInt|toFloat|toDouble"), pattern("Short.toShort|toInt|toFloat|toDouble"), pattern("Byte.toByte|toShort|toInt|toFloat|toDouble"), pattern("Float|Double.toFloat|toDouble"), @@ -40,16 +40,26 @@ public object NumberConversionFIF : CompositeFIF() { "Int|Short|Byte.toLong" to ConversionUnaryIntrinsic { longFromInt(it) }, "Float|Double.toLong" to ConversionUnaryIntrinsic { longFromNumber(it) }, + "Char.toDouble|toFloat|toInt" to ConversionUnaryIntrinsic { charToInt(it) }, + "Char.toShort" to ConversionUnaryIntrinsic { toShort(charToInt(it)) }, + "Char.toByte" to ConversionUnaryIntrinsic { toByte(charToInt(it)) }, + "Char.toLong" to ConversionUnaryIntrinsic { longFromInt(charToInt(it)) }, + "Number.toInt" to ConversionUnaryIntrinsic { invokeKotlinFunction("numberToInt", it) }, "Number.toShort" to ConversionUnaryIntrinsic { invokeKotlinFunction("numberToShort", it) }, "Number.toByte" to ConversionUnaryIntrinsic { invokeKotlinFunction("numberToByte", it) }, + "Number.toChar" to ConversionUnaryIntrinsic { invokeKotlinFunction("numberToChar", it) }, "Number.toFloat|toDouble" to ConversionUnaryIntrinsic { invokeKotlinFunction("numberToDouble", it) }, "Number.toLong" to ConversionUnaryIntrinsic { invokeKotlinFunction("numberToLong", it) }, + "Int|Short|Byte|Float|Double.toChar" to ConversionUnaryIntrinsic { toChar(it) }, + "Long.toFloat|toDouble" to ConversionUnaryIntrinsic { invokeMethod(it, "toNumber") }, "Long.toInt" to ConversionUnaryIntrinsic { invokeMethod(it, "toInt") }, "Long.toShort" to ConversionUnaryIntrinsic { toShort(invokeMethod(it, "toInt")) }, - "Long.toByte" to ConversionUnaryIntrinsic { toByte(invokeMethod(it, "toInt")) } + "Long.toByte" to ConversionUnaryIntrinsic { toByte(invokeMethod(it, "toInt")) }, + "Long.toChar" to ConversionUnaryIntrinsic { toChar(invokeMethod(it, "toInt")) } + ) class ConversionUnaryIntrinsic(val applyFun: (receiver: JsExpression) -> JsExpression) : FunctionIntrinsic() { diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/PrimitiveBinaryOperationFIF.java b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/PrimitiveBinaryOperationFIF.java index 171ea9ca124..17c4252dac1 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/PrimitiveBinaryOperationFIF.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/PrimitiveBinaryOperationFIF.java @@ -69,6 +69,16 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory { } }; + @NotNull + private static final BinaryOperationInstrinsicBase CHAR_RANGE_TO_INTRINSIC = new BinaryOperationInstrinsicBase() { + @NotNull + @Override + public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) { + //TODO: add tests and correct expression for reversed ranges. + return JsAstUtils.charRangeTo(left, right); + } + }; + @NotNull private static final BinaryOperationInstrinsicBase INTEGER_DIVISION_INTRINSIC = new BinaryOperationInstrinsicBase() { @NotNull @@ -124,6 +134,18 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory { @Nullable @Override public FunctionIntrinsic getIntrinsic(@NotNull FunctionDescriptor descriptor) { + if (pattern("Int|Short|Byte|Double|Float.compareTo(Char)").apply(descriptor)) { + return new WithCharAsSecondOperandFunctionIntrinsic(PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC); + } + + if (pattern("Char.compareTo(Int|Short|Byte|Double|Float)").apply(descriptor)) { + return new WithCharAsFirstOperandFunctionIntrinsic(PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC); + } + + if (pattern("Char.rangeTo(Char)").apply(descriptor)) { + return CHAR_RANGE_TO_INTRINSIC; + } + if (PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS.apply(descriptor)) { return PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC; } @@ -141,6 +163,12 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory { if (pattern("Int|Short|Byte.div(Int|Short|Byte)").apply(descriptor)) { return INTEGER_DIVISION_INTRINSIC; } + if (pattern("Char.div(Int|Short|Byte)").apply(descriptor)) { + return new WithCharAsFirstOperandFunctionIntrinsic(INTEGER_DIVISION_INTRINSIC); + } + if (pattern("Int|Short|Byte.div(Char)").apply(descriptor)) { + return new WithCharAsSecondOperandFunctionIntrinsic(INTEGER_DIVISION_INTRINSIC); + } if (descriptor.getName().equals(Name.identifier("rangeTo"))) { return RANGE_TO_INTRINSIC; } @@ -151,7 +179,15 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory { } } JsBinaryOperator operator = getOperator(descriptor); - return new PrimitiveBinaryOperationFunctionIntrinsic(operator); + BinaryOperationInstrinsicBase result = new PrimitiveBinaryOperationFunctionIntrinsic(operator); + + if (pattern("Char.plus|minus|times|div|mod(Int|Short|Byte|Double|Float)").apply(descriptor)) { + return new WithCharAsFirstOperandFunctionIntrinsic(result); + } + if (pattern("Int|Short|Byte|Double|Float.plus|minus|times|div|mod(Char)").apply(descriptor)) { + return new WithCharAsSecondOperandFunctionIntrinsic(result); + } + return result; } @NotNull @@ -182,4 +218,36 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory { return new JsBinaryOperation(operator, left, right); } } + + private static class WithCharAsFirstOperandFunctionIntrinsic extends BinaryOperationInstrinsicBase { + + @NotNull + private final BinaryOperationInstrinsicBase functionIntrinsic; + + private WithCharAsFirstOperandFunctionIntrinsic(@NotNull BinaryOperationInstrinsicBase functionIntrinsic) { + this.functionIntrinsic = functionIntrinsic; + } + + @NotNull + @Override + public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) { + return functionIntrinsic.doApply(JsAstUtils.charToInt(left), right, context); + } + } + + private static class WithCharAsSecondOperandFunctionIntrinsic extends BinaryOperationInstrinsicBase { + + @NotNull + private final BinaryOperationInstrinsicBase functionIntrinsic; + + private WithCharAsSecondOperandFunctionIntrinsic(@NotNull BinaryOperationInstrinsicBase functionIntrinsic) { + this.functionIntrinsic = functionIntrinsic; + } + + @NotNull + @Override + public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) { + return functionIntrinsic.doApply(left, JsAstUtils.charToInt(right), context); + } + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/PrimitiveUnaryOperationFIF.java b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/PrimitiveUnaryOperationFIF.java index 90eb38341fe..91ec9f79982 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/PrimitiveUnaryOperationFIF.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/PrimitiveUnaryOperationFIF.java @@ -30,10 +30,12 @@ import org.jetbrains.k2js.translate.intrinsic.functions.basic.FunctionIntrinsic; import org.jetbrains.k2js.translate.intrinsic.functions.patterns.DescriptorPredicate; import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate; import org.jetbrains.k2js.translate.operation.OperatorTable; +import org.jetbrains.k2js.translate.utils.JsAstUtils; import org.jetbrains.k2js.translate.utils.JsDescriptorUtils; import java.util.List; +import static org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate.PRIMITIVE_NUMBERS; import static org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder.pattern; public enum PrimitiveUnaryOperationFIF implements FunctionIntrinsicFactory { @@ -94,6 +96,67 @@ public enum PrimitiveUnaryOperationFIF implements FunctionIntrinsicFactory { } }; + private static abstract class UnaryOperationInstrinsicBase extends FunctionIntrinsic { + @NotNull + public abstract JsExpression doApply(@NotNull JsExpression receiver, @NotNull TranslationContext context); + + @NotNull + @Override + public JsExpression apply( + @Nullable JsExpression receiver, + @NotNull List arguments, + @NotNull TranslationContext context + ) { + assert receiver != null; + assert arguments.size() == 0; + return doApply(receiver, context); + } + } + + @NotNull + private static final FunctionIntrinsic CHAR_PLUS = new UnaryOperationInstrinsicBase() { + @NotNull + @Override + public JsExpression doApply( + @NotNull JsExpression receiver, @NotNull TranslationContext context + ) { + return JsAstUtils.charToInt(receiver); + } + }; + + @NotNull + private static final FunctionIntrinsic CHAR_MINUS = new UnaryOperationInstrinsicBase() { + @NotNull + @Override + public JsExpression doApply( + @NotNull JsExpression receiver, @NotNull TranslationContext context + ) { + return new JsPrefixOperation(JsUnaryOperator.NEG, JsAstUtils.charToInt(receiver)); + } + }; + + @NotNull + private static final FunctionIntrinsic CHAR_INC = new UnaryOperationInstrinsicBase() { + @NotNull + @Override + public JsExpression doApply( + @NotNull JsExpression receiver, @NotNull TranslationContext context + ) { + return JsAstUtils.invokeKotlinFunction("charInc", receiver); + } + }; + + @NotNull + private static final FunctionIntrinsic CHAR_DEC = new UnaryOperationInstrinsicBase() { + @NotNull + @Override + public JsExpression doApply( + @NotNull JsExpression receiver, @NotNull TranslationContext context + ) { + return JsAstUtils.invokeKotlinFunction("charDec", receiver); + } + }; + @Nullable @Override public FunctionIntrinsic getIntrinsic(@NotNull FunctionDescriptor descriptor) { @@ -101,6 +164,19 @@ public enum PrimitiveUnaryOperationFIF implements FunctionIntrinsicFactory { return null; } + if (pattern("Char.plus()").apply(descriptor)) { + return CHAR_PLUS; + } + if (pattern("Char.minus()").apply(descriptor)) { + return CHAR_MINUS; + } + if (pattern("Char.inc()").apply(descriptor)) { + return CHAR_INC; + } + if (pattern("Char.dec()").apply(descriptor)) { + return CHAR_DEC; + } + if (INC_OPERATION_FOR_PRIMITIVE_NUMBER.apply(descriptor)) { return NUMBER_INC_INTRINSIC; } @@ -108,6 +184,7 @@ public enum PrimitiveUnaryOperationFIF implements FunctionIntrinsicFactory { return NUMBER_DEC_INTRINSIC; } + Name name = descriptor.getName(); JsUnaryOperator jsOperator; diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/patterns/NamePredicate.java b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/patterns/NamePredicate.java index ce4f7d41e13..f5578b03f95 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/patterns/NamePredicate.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/patterns/NamePredicate.java @@ -59,6 +59,9 @@ public final class NamePredicate implements Predicate { @NotNull public static final NamePredicate NUMBER = new NamePredicate("Number"); + @NotNull + public static final NamePredicate CHAR = new NamePredicate(PrimitiveType.CHAR.getTypeName()); + @NotNull public static final NamePredicate LONG = new NamePredicate(PrimitiveType.LONG.getTypeName()); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/CompareToBOIF.kt b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/CompareToBOIF.kt index 234502585aa..512eb70be63 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/CompareToBOIF.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/CompareToBOIF.kt @@ -31,6 +31,8 @@ import org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken object CompareToBOIF : BinaryOperationIntrinsicFactory { + val COMPARE_TO_CHAR = pattern("Int|Short|Byte|Double|Float.compareTo(Char)") + val CHAR_COMPARE_TO = pattern("Char.compareTo(Int|Short|Byte|Double|Float)") val PRIMITIVE_COMPARE_TO = pattern("Int|Short|Byte|Double|Float|Char|String.compareTo") private object CompareToIntrinsic : AbstractBinaryOperationIntrinsic() { @@ -40,6 +42,20 @@ object CompareToBOIF : BinaryOperationIntrinsicFactory { } } + private object CompareToCharIntrinsic : AbstractBinaryOperationIntrinsic() { + override fun apply(expression: JetBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression { + val operator = OperatorTable.getBinaryOperator(getOperationToken(expression)) + return JsBinaryOperation(operator, left, JsAstUtils.charToInt(right)) + } + } + + private object CompareCharToPrimitiveIntrinsic : AbstractBinaryOperationIntrinsic() { + override fun apply(expression: JetBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression { + val operator = OperatorTable.getBinaryOperator(getOperationToken(expression)) + return JsBinaryOperation(operator, JsAstUtils.charToInt(left), right) + } + } + private object CompareToFunctionIntrinsic : AbstractBinaryOperationIntrinsic() { override fun apply(expression: JetBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression { val operator = OperatorTable.getBinaryOperator(getOperationToken(expression)) @@ -53,6 +69,10 @@ object CompareToBOIF : BinaryOperationIntrinsicFactory { override public fun getIntrinsic(descriptor: FunctionDescriptor): BinaryOperationIntrinsic? { if (JsDescriptorUtils.isBuiltin(descriptor)) when { + COMPARE_TO_CHAR.apply(descriptor) -> + return CompareToCharIntrinsic + CHAR_COMPARE_TO.apply(descriptor) -> + return CompareCharToPrimitiveIntrinsic PRIMITIVE_COMPARE_TO.apply(descriptor) -> return CompareToIntrinsic else -> diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/LongCompareToBOIF.kt b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/LongCompareToBOIF.kt index 3f58e4fcb31..1009f353863 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/LongCompareToBOIF.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/operation/LongCompareToBOIF.kt @@ -36,7 +36,9 @@ public object LongCompareToBOIF : BinaryOperationIntrinsicFactory { val FLOATING_POINT_COMPARE_TO_LONG_PATTERN = pattern("Double|Float.compareTo(Long)") val LONG_COMPARE_TO_FLOATING_POINT_PATTERN = pattern("Long.compareTo(Float|Double)") val INTEGER_COMPARE_TO_LONG_PATTERN = pattern("Int|Short|Byte.compareTo(Long)") + val CHAR_COMPARE_TO_LONG_PATTERN = pattern("Char.compareTo(Long)") val LONG_COMPARE_TO_INTEGER_PATTERN = pattern("Long.compareTo(Int|Short|Byte)") + val LONG_COMPARE_TO_CHAR_PATTERN = pattern("Long.compareTo(Char)") val LONG_COMPARE_TO_LONG_PATTERN = pattern("Long.compareTo(Long)") private object FLOATING_POINT_COMPARE_TO_LONG : AbstractBinaryOperationIntrinsic() { @@ -62,7 +64,9 @@ public object LongCompareToBOIF : BinaryOperationIntrinsicFactory { } private val INTEGER_COMPARE_TO_LONG = CompareToBinaryIntrinsic( { longFromInt(it) }, ID) + private val CHAR_COMPARE_TO_LONG = CompareToBinaryIntrinsic( { longFromInt(charToInt(it)) }, ID) private val LONG_COMPARE_TO_INTEGER = CompareToBinaryIntrinsic( ID, { longFromInt(it) }) + private val LONG_COMPARE_TO_CHAR = CompareToBinaryIntrinsic( ID, { longFromInt(charToInt(it)) }) private val LONG_COMPARE_TO_LONG = CompareToBinaryIntrinsic( ID, ID ) override public fun getSupportTokens(): ImmutableSet = OperatorConventions.COMPARISON_OPERATIONS @@ -73,7 +77,9 @@ public object LongCompareToBOIF : BinaryOperationIntrinsicFactory { FLOATING_POINT_COMPARE_TO_LONG_PATTERN.apply(descriptor) -> FLOATING_POINT_COMPARE_TO_LONG LONG_COMPARE_TO_FLOATING_POINT_PATTERN.apply(descriptor) -> LONG_COMPARE_TO_FLOATING_POINT INTEGER_COMPARE_TO_LONG_PATTERN.apply(descriptor) -> INTEGER_COMPARE_TO_LONG + CHAR_COMPARE_TO_LONG_PATTERN.apply(descriptor) -> CHAR_COMPARE_TO_LONG LONG_COMPARE_TO_INTEGER_PATTERN.apply(descriptor) -> LONG_COMPARE_TO_INTEGER + LONG_COMPARE_TO_CHAR_PATTERN.apply(descriptor) -> LONG_COMPARE_TO_CHAR LONG_COMPARE_TO_LONG_PATTERN.apply(descriptor) -> LONG_COMPARE_TO_LONG else -> null } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java index 009944f8a6b..2a53e76f758 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/JsAstUtils.java @@ -133,6 +133,11 @@ public final class JsAstUtils { return new JsBinaryOperation(JsBinaryOperator.BIT_OR, expression, JsNumberLiteral.ZERO); } + @NotNull + public static JsExpression charToInt(@NotNull JsExpression expression) { + return invokeMethod(expression, "charCodeAt", JsNumberLiteral.ZERO); + } + @NotNull public static JsExpression toShort(@NotNull JsExpression expression) { return invokeKotlinFunction(OperatorConventions.SHORT.getIdentifier(), expression); @@ -148,6 +153,11 @@ public final class JsAstUtils { return invokeKotlinFunction(OperatorConventions.LONG.getIdentifier(), expression); } + @NotNull + public static JsExpression toChar(@NotNull JsExpression expression) { + return invokeKotlinFunction(OperatorConventions.CHAR.getIdentifier(), expression); + } + @NotNull public static JsExpression compareTo(@NotNull JsExpression left, @NotNull JsExpression right) { return invokeKotlinFunction(OperatorConventions.COMPARE_TO.getIdentifier(), left, right); @@ -163,6 +173,11 @@ public final class JsAstUtils { return invokeKotlinFunction(Namer.IS_NUMBER, expression); } + @NotNull + public static JsExpression isChar(@NotNull JsExpression expression) { + return invokeKotlinFunction(Namer.IS_CHAR, expression); + } + @NotNull private static JsExpression rangeTo(@NotNull String rangeClassName, @NotNull JsExpression rangeStart, @NotNull JsExpression rangeEnd) { JsNameRef expr = new JsNameRef(rangeClassName, Namer.KOTLIN_NAME); @@ -176,6 +191,11 @@ public final class JsAstUtils { return rangeTo(Namer.NUMBER_RANGE, rangeStart, rangeEnd); } + @NotNull + public static JsExpression charRangeTo(@NotNull JsExpression rangeStart, @NotNull JsExpression rangeEnd) { + return rangeTo(Namer.CHAR_RANGE, rangeStart, rangeEnd); + } + @NotNull public static final JsNameRef kotlinLongNameRef = new JsNameRef("Long", Namer.KOTLIN_OBJECT_REF); diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java index f84b2ac8bca..df1049fb5f8 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/utils/TranslationUtils.java @@ -389,7 +389,8 @@ public final class TranslationUtils { if (operationDescriptor == null || !(operationDescriptor instanceof FunctionDescriptor)) return true; JetType returnType = operationDescriptor.getReturnType(); - if (returnType != null && returnType.equals(KotlinBuiltIns.getInstance().getLongType())) return false; + if (returnType != null && + KotlinBuiltIns.getInstance().getCharType().equals(returnType) || (KotlinBuiltIns.getInstance().getLongType().equals(returnType))) return false; if (context.intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) operationDescriptor).exists()) return true; diff --git a/js/js.translator/testData/kotlin_lib.js b/js/js.translator/testData/kotlin_lib.js index df63cb8af93..e420a0f3961 100644 --- a/js/js.translator/testData/kotlin_lib.js +++ b/js/js.translator/testData/kotlin_lib.js @@ -86,8 +86,15 @@ }; Kotlin.compareTo = function (a, b) { - var type = typeof a; - if (type == "number" || type == "string") { + var typeA = typeof a; + var typeB = typeof a; + if (Kotlin.isChar(a) && typeB == "number") { + return Kotlin.primitiveCompareTo(a.charCodeAt(0), b); + } + if (typeA == "number" && Kotlin.isChar(b)) { + return Kotlin.primitiveCompareTo(a, b.charCodeAt(0)); + } + if (typeA == "number" || typeA == "string") { return a < b ? -1 : a > b ? 1 : 0; } return a.compareTo_za3rmp$(b); @@ -101,8 +108,16 @@ return typeof a == "number" || a instanceof Kotlin.Long; }; - Kotlin.toInt = function (a) { - return a | 0; + Kotlin.isChar = function (value) { + return (typeof value) == "string" && value.length == 1; + }; + + Kotlin.charInc = function (value) { + return String.fromCharCode(value.charCodeAt(0)+1); + }; + + Kotlin.charDec = function (value) { + return String.fromCharCode(value.charCodeAt(0)-1); }; Kotlin.toShort = function (a) { @@ -113,6 +128,10 @@ return (a & 0xFF) << 24 >> 24; }; + Kotlin.toChar = function (a) { + return String.fromCharCode((((a | 0) % 65536) & 0xFFFF) << 16 >>> 16); + }; + Kotlin.numberToLong = function (a) { return a instanceof Kotlin.Long ? a : Kotlin.Long.fromNumber(a); }; @@ -133,6 +152,10 @@ return +a; }; + Kotlin.numberToChar = function (a) { + return Kotlin.toChar(Kotlin.numberToInt(a)); + }; + Kotlin.intUpto = function (from, to) { return new Kotlin.NumberRange(from, to); }; @@ -647,6 +670,50 @@ } }); + Kotlin.CharRangeIterator = Kotlin.createClassNow(Kotlin.RangeIterator, + function (start, end, increment) { + Kotlin.RangeIterator.call(this, start, end, increment); + }, { + next: function () { + var value = this.i; + this.i = this.i + this.increment; + return String.fromCharCode(value); + }, + }); + + Kotlin.CharRange = Kotlin.createClassNow(null, + function (start, end) { + this.start = start.charCodeAt(0); + this.end = end.charCodeAt(0); + this.increment = 1; + }, { + contains: function (char) { + var code = char.charCodeAt(0) + return this.start <= code && code <= this.end; + }, + iterator: function () { + return new Kotlin.CharRangeIterator(this.start, this.end, this.increment); + }, + isEmpty: function () { + return this.start > this.end; + }, + equals_za3rmp$: isSameNotNullRanges + }); + + Kotlin.CharNumberProgression = Kotlin.createClassNow(null, + function (start, end, increment) { + this.start = start.charCodeAt(0); + this.end = end.charCodeAt(0); + this.increment = increment; + }, { + iterator: function () { + return new Kotlin.CharRangeIterator(this.start, this.end, this.increment); + }, + isEmpty: function() { + return this.increment > 0 ? this.start > this.end : this.start < this.end; + } + }); + /** * @interface * @template T