From 9233e6c17683a2a25bc1a731d5cddfd027e0f7b4 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Mon, 9 Jul 2018 22:15:42 +0300 Subject: [PATCH] JS IR: char (always boxed for now) --- .../kotlin/ir/backend/js/JsIntrinsics.kt | 3 + .../js/lower/IntrinsicifyCallsLowering.kt | 36 ++++++- .../backend/js/lower/TypeOperatorLowering.kt | 8 +- .../IrElementToJsExpressionTransformer.kt | 2 +- .../kotlin/ir/types/irTypePredicates.kt | 1 + .../kotlin/js/test/BasicIrBoxTest.kt | 1 + libraries/stdlib/js/irRuntime/char.kt | 95 +++++++++++++++++++ libraries/stdlib/js/irRuntime/core.kt | 2 +- .../stdlib/js/irRuntime/typeCheckUtils.kt | 2 +- 9 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 libraries/stdlib/js/irRuntime/char.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 46f6db1aef8..5b964d4762e 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 @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.ir.backend.js +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl @@ -128,6 +129,8 @@ class JsIntrinsics( ) ) + val charConstructor = context.symbolTable.referenceConstructor(context.getClass(KotlinBuiltIns.FQ_NAMES._char.toSafe()).constructors.single()) + // Helpers: private fun getInternalFunction(name: String) = diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt index fe7d9aad4cb..19672432350 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt @@ -152,6 +152,27 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL op(type, OperatorNames.MOD, withLongCoercion(intrinsics.jsMod)) op(type, OperatorNames.REM, withLongCoercion(intrinsics.jsMod)) } + + for (type in arrayOf(irBuiltIns.byteType, irBuiltIns.intType)) { + op(type, ConversionNames.TO_CHAR) { + irCall(it, intrinsics.charConstructor, dispatchReceiverAsFirstArgument = true) + } + } + + for (type in arrayOf(irBuiltIns.floatType, irBuiltIns.doubleType)) { + op(type, ConversionNames.TO_CHAR) { + IrCallImpl( + it.startOffset, + it.endOffset, + irBuiltIns.charType, + intrinsics.charConstructor + ).apply { + putValueArgument(0, irCall(it, intrinsics.jsNumberToInt, dispatchReceiverAsFirstArgument = true)) + } + } + } + + op(irBuiltIns.charType, ConversionNames.TO_CHAR) { it.dispatchReceiver!! } } nameToTransformer.run { @@ -210,12 +231,21 @@ class IntrinsicifyCallsLowering(private val context: JsIrBackendContext) : FileL return IrCallImpl( expression.startOffset, expression.endOffset, - context.intrinsics.longConstructor.owner.returnType, + irBuiltIns.longType, context.intrinsics.longConstructor ).apply { putValueArgument(0, JsIrBuilder.buildInt(context.irBuiltIns.intType, low)) putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, high)) } + } else if (expression.kind is IrConstKind.Char) { + return IrCallImpl( + expression.startOffset, + expression.endOffset, + irBuiltIns.charType, + context.intrinsics.charConstructor + ).apply { + putValueArgument(0, JsIrBuilder.buildInt(context.irBuiltIns.intType, IrConstKind.Char.valueOf(expression).toInt())) + } } return super.visitConst(expression) } @@ -490,9 +520,9 @@ fun translateEqualsForNullableBoolean(rhs: IrType): EqualityLoweringType = when } -private fun IrType.isNullableJsNumber(): Boolean = isNullablePrimitiveType() && !isNullableLong() +private fun IrType.isNullableJsNumber(): Boolean = isNullablePrimitiveType() && !isNullableLong() && !isNullableChar() -private fun IrType.isJsNumber(): Boolean = isPrimitiveType() && !isLong() +private fun IrType.isJsNumber(): Boolean = isPrimitiveType() && !isLong() && !isChar() // TODO extract to common place? diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt index 749855010c1..5cbdf1cff94 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt @@ -45,7 +45,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { private val isInterfaceSymbol = getInternalFunction("isInterface") private val isArraySymbol = getInternalFunction("isArray") - private val isCharSymbol = getInternalFunction("isChar") +// private val isCharSymbol = getInternalFunction("isChar") private val isObjectSymbol = getInternalFunction("isObject") private val instanceOfIntrinsicSymbol = context.intrinsics.jsInstanceOf.symbol @@ -199,7 +199,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { return when { toType.isAny() -> generateIsObjectCheck(argument) isTypeOfCheckingType(toType) -> generateTypeOfCheck(argument, toType) - toType.isChar() -> generateCheckForChar(argument) +// toType.isChar() -> generateCheckForChar(argument) toType.isArray() -> generateGenericArrayCheck(argument) toType.isPrimitiveArray() -> generatePrimitiveArrayTypeCheck(argument, toType) toType.isTypeParameter() -> generateTypeCheckWithTypeParameter(argument, toType) @@ -224,8 +224,8 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { } } - private fun generateCheckForChar(argument: IrExpression) = - JsIrBuilder.buildCall(isCharSymbol).apply { dispatchReceiver = argument } +// private fun generateCheckForChar(argument: IrExpression) = +// JsIrBuilder.buildCall(isCharSymbol).apply { dispatchReceiver = argument } private fun generateTypeOfCheck(argument: IrExpression, toType: IrType): IrExpression { val marker = when { 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 8d5f57f483d..51a577a7411 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 @@ -83,7 +83,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer JsIntLiteral(kind.valueOf(expression).toInt()) is IrConstKind.Int -> JsIntLiteral(kind.valueOf(expression)) is IrConstKind.Long -> throw IllegalStateException("Long const should have been lowered at this point") - is IrConstKind.Char -> TODO("Char const") + is IrConstKind.Char -> throw IllegalStateException("Char const should have been lowered at this point") is IrConstKind.Float -> JsDoubleLiteral(kind.valueOf(expression).toDouble()) is IrConstKind.Double -> JsDoubleLiteral(kind.valueOf(expression)) } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt index e0c4c9f64b7..92708be5101 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt @@ -62,3 +62,4 @@ fun IrType.isNumber(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.numb fun IrType.isNullableBoolean(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES._boolean) fun IrType.isNullableLong(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES._long) +fun IrType.isNullableChar(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES._char) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt index b9c504211e8..9c441935025 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt @@ -51,6 +51,7 @@ abstract class BasicIrBoxTest( "libraries/stdlib/js/irRuntime/numberConversion.kt", "libraries/stdlib/js/irRuntime/compareTo.kt", "libraries/stdlib/js/irRuntime/annotations.kt", + "libraries/stdlib/js/irRuntime/char.kt", "libraries/stdlib/js/irRuntime/DefaultConstructorMarker.kt", "libraries/stdlib/js/irRuntime/exceptions.kt", "libraries/stdlib/js/irRuntime/internalAnnotations.kt", diff --git a/libraries/stdlib/js/irRuntime/char.kt b/libraries/stdlib/js/irRuntime/char.kt new file mode 100644 index 00000000000..0e0f6c428a8 --- /dev/null +++ b/libraries/stdlib/js/irRuntime/char.kt @@ -0,0 +1,95 @@ +/* + * 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 + +/** + * Represents a 16-bit Unicode character. + * On the JVM, non-nullable values of this type are represented as values of the primitive type `char`. + */ +public class Char(value: Int) : Comparable { + + private val value = value and 0xFFFF + + /** + * Compares this value with the specified value for order. + * Returns zero if this value is equal to the specified other value, a negative number if it's less than other, + * or a positive number if it's greater than other. + */ + public override fun compareTo(other: Char): Int = value - other.value + + /** Adds the other Int value to this value resulting a Char. */ + public operator fun plus(other: Int): Char = Char(value + other) + + /** Subtracts the other Char value from this value resulting an Int. */ + public operator fun minus(other: Char): Int = value - other.value + /** Subtracts the other Int value from this value resulting a Char. */ + public operator fun minus(other: Int): Char = Char(value - other) + + /** Increments this value. */ + public operator fun inc(): Char = Char(value + 1) + /** Decrements this value. */ + public operator fun dec(): Char = Char(value - 1) + + /** Creates a range from this value to the specified [other] value. */ + public operator fun rangeTo(other: Char): CharRange = null!! // TODO + + /** Returns the value of this character as a `Byte`. */ + public fun toByte(): Byte = value.toByte() + /** Returns the value of this character as a `Char`. */ + public fun toChar(): Char = this + /** Returns the value of this character as a `Short`. */ + public fun toShort(): Short = value.toShort() + /** Returns the value of this character as a `Int`. */ + public fun toInt(): Int = value + /** Returns the value of this character as a `Long`. */ + public fun toLong(): Long = value.toLong() + /** Returns the value of this character as a `Float`. */ + public fun toFloat(): Float = value.toFloat() + /** Returns the value of this character as a `Double`. */ + public fun toDouble(): Double = value.toDouble() + + override fun equals(other: Any?): Boolean = other is Char && value == other.value + + override fun hashCode(): Int = value + + override fun toString(): String { + val value = value + return js("String.fromCharCode(value)").unsafeCast() + } + + companion object { + /** + * The minimum value of a Unicode high-surrogate code unit. + */ + public const val MIN_HIGH_SURROGATE: Char = '\uD800' + + /** + * The maximum value of a Unicode high-surrogate code unit. + */ + public const val MAX_HIGH_SURROGATE: Char = '\uDBFF' + + /** + * The minimum value of a Unicode low-surrogate code unit. + */ + public const val MIN_LOW_SURROGATE: Char = '\uDC00' + + /** + * The maximum value of a Unicode low-surrogate code unit. + */ + public const val MAX_LOW_SURROGATE: Char = '\uDFFF' + + /** + * The minimum value of a Unicode surrogate code unit. + */ + public const val MIN_SURROGATE: Char = MIN_HIGH_SURROGATE + + /** + * The maximum value of a Unicode surrogate code unit. + */ + public const val MAX_SURROGATE: Char = MAX_LOW_SURROGATE + } + +} \ No newline at end of file diff --git a/libraries/stdlib/js/irRuntime/core.kt b/libraries/stdlib/js/irRuntime/core.kt index 4996f1b88c5..9e30fa8eaaf 100644 --- a/libraries/stdlib/js/irRuntime/core.kt +++ b/libraries/stdlib/js/irRuntime/core.kt @@ -65,7 +65,7 @@ fun hashCode(obj: dynamic): Int { var POW_2_32 = 4294967296; // TODO: consider switching to Symbol type once we are on ES6. /** @const */ - var OBJECT_HASH_CODE_PROPERTY_NAME = "kotlinHashCodeValue${'$'}"; + var OBJECT_HASH_CODE_PROPERTY_NAME = "kotlinHashCodeValue${"$"}"; function getObjectHashCode(obj) { if (!(OBJECT_HASH_CODE_PROPERTY_NAME in obj)) { diff --git a/libraries/stdlib/js/irRuntime/typeCheckUtils.kt b/libraries/stdlib/js/irRuntime/typeCheckUtils.kt index f943bc0eee5..f857ab662f8 100644 --- a/libraries/stdlib/js/irRuntime/typeCheckUtils.kt +++ b/libraries/stdlib/js/irRuntime/typeCheckUtils.kt @@ -14,7 +14,7 @@ private fun isInterfaceImpl(ctor: dynamic, iface: dynamic): Boolean { val self = ::isInterfaceImpl return js( """ - var metadata = ctor.${'$'}metadata${'$'}; + var metadata = ctor.${"$"}metadata${"$"}; if (metadata != null) { var interfaces = metadata.interfaces; for (var i = 0; i < interfaces.length; i++) {