[JS IR BE] Covert Char into inline class
This commit is contained in:
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
|
|||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
|
||||||
|
|
||||||
private fun FileLoweringPass.lower(moduleFragment: IrModuleFragment) = moduleFragment.files.forEach { lower(it) }
|
private fun FileLoweringPass.lower(moduleFragment: IrModuleFragment) = moduleFragment.files.forEach { lower(it) }
|
||||||
|
|
||||||
@@ -371,13 +370,13 @@ val jsPhases = listOf(
|
|||||||
TypeOperatorLoweringPhase,
|
TypeOperatorLoweringPhase,
|
||||||
SecondaryConstructorLoweringPhase,
|
SecondaryConstructorLoweringPhase,
|
||||||
SecondaryFactoryInjectorLoweringPhase,
|
SecondaryFactoryInjectorLoweringPhase,
|
||||||
|
ClassReferenceLoweringPhase,
|
||||||
InlineClassLoweringPhase,
|
InlineClassLoweringPhase,
|
||||||
AutoboxingTransformerPhase,
|
AutoboxingTransformerPhase,
|
||||||
BlockDecomposerLoweringPhase,
|
BlockDecomposerLoweringPhase,
|
||||||
ClassReferenceLoweringPhase,
|
|
||||||
PrimitiveCompanionLoweringPhase,
|
PrimitiveCompanionLoweringPhase,
|
||||||
ConstLoweringPhase,
|
ConstLoweringPhase,
|
||||||
CallsLoweringPhase,
|
CallsLoweringPhase,
|
||||||
IrModuleEndPhase,
|
IrModuleEndPhase,
|
||||||
IrToJsPhase
|
IrToJsPhase
|
||||||
)
|
)
|
||||||
|
|||||||
+7
-4
@@ -6,14 +6,15 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.util.irCall
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.util.irCall
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
|
||||||
@@ -29,9 +30,11 @@ private class ArrayConstructorTransformer(
|
|||||||
val context: JsIrBackendContext
|
val context: JsIrBackendContext
|
||||||
) : IrElementTransformerVoid() {
|
) : IrElementTransformerVoid() {
|
||||||
|
|
||||||
private val primitiveArrayInlineToSizeConstructorMap = context.intrinsics.primitiveArrays.keys.associate {
|
// Inline constructor for CharArray is implemented in runtime
|
||||||
it.inlineConstructor to it.sizeConstructor
|
private val primitiveArrayInlineToSizeConstructorMap =
|
||||||
}
|
context.intrinsics.primitiveArrays.filter { it.value != PrimitiveType.CHAR }.keys.associate {
|
||||||
|
it.inlineConstructor to it.sizeConstructor
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
|
|||||||
+2
-1
@@ -151,7 +151,8 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
|
|||||||
|
|
||||||
override fun IrExpression.useAsVarargElement(expression: IrVararg): IrExpression {
|
override fun IrExpression.useAsVarargElement(expression: IrVararg): IrExpression {
|
||||||
return this.useAs(
|
return this.useAs(
|
||||||
if (this.type.isInlined())
|
// Do not box primitive inline classes
|
||||||
|
if (this.type.isInlined() && !expression.type.isInlined() && !expression.type.isPrimitiveArray())
|
||||||
irBuiltIns.anyNType
|
irBuiltIns.anyNType
|
||||||
else
|
else
|
||||||
expression.varargElementType
|
expression.varargElementType
|
||||||
|
|||||||
+68
-14
@@ -8,13 +8,22 @@ package org.jetbrains.kotlin.ir.backend.js.lower
|
|||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrSpreadElement
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrVararg
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||||
|
import org.jetbrains.kotlin.ir.util.constructors
|
||||||
|
import org.jetbrains.kotlin.ir.util.defaultType
|
||||||
|
import org.jetbrains.kotlin.ir.util.getInlineClassBackingField
|
||||||
|
import org.jetbrains.kotlin.ir.util.getInlinedClass
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
|
||||||
@@ -44,32 +53,64 @@ private class VarargTransformer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun IrExpression.unboxInlineClassIfNeeded(): IrExpression {
|
||||||
|
val inlinedClass = type.getInlinedClass() ?: return this
|
||||||
|
val field = getInlineClassBackingField(inlinedClass)
|
||||||
|
return IrGetFieldImpl(startOffset, endOffset, field.symbol, inlinedClass.defaultType, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IrExpression.boxInlineClassIfNeeded(inlineClass: IrClass?) =
|
||||||
|
if (inlineClass == null)
|
||||||
|
this
|
||||||
|
else
|
||||||
|
IrCallImpl(startOffset, endOffset, inlineClass.defaultType, inlineClass.constructors.single { it.isPrimary }.symbol).also {
|
||||||
|
it.putValueArgument(0, this)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitVararg(expression: IrVararg): IrExpression {
|
override fun visitVararg(expression: IrVararg): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
val currentList = mutableListOf<IrExpression>()
|
val currentList = mutableListOf<IrExpression>()
|
||||||
val segments = mutableListOf<IrExpression>()
|
val segments = mutableListOf<IrExpression>()
|
||||||
|
|
||||||
|
val elementType = expression.varargElementType
|
||||||
|
val primitiveElementType: IrType
|
||||||
|
val primitiveExpressionType: IrType
|
||||||
|
val needUnboxing: Boolean
|
||||||
|
val arrayInlineClass = expression.type.getInlinedClass()
|
||||||
|
if (arrayInlineClass != null) {
|
||||||
|
primitiveElementType = getInlineClassBackingField(elementType.getInlinedClass()!!).type
|
||||||
|
primitiveExpressionType = getInlineClassBackingField(arrayInlineClass).type
|
||||||
|
needUnboxing = true
|
||||||
|
} else {
|
||||||
|
primitiveElementType = elementType
|
||||||
|
primitiveExpressionType = expression.type
|
||||||
|
needUnboxing = false
|
||||||
|
}
|
||||||
|
|
||||||
for (e in expression.elements) {
|
for (e in expression.elements) {
|
||||||
if (e is IrSpreadElement) {
|
when (e) {
|
||||||
if (!currentList.isEmpty()) {
|
is IrSpreadElement -> {
|
||||||
segments.add(currentList.toArrayLiteral(expression.type, expression.varargElementType))
|
if (!currentList.isEmpty()) {
|
||||||
currentList.clear()
|
segments.add(currentList.toArrayLiteral(primitiveExpressionType, primitiveElementType))
|
||||||
|
currentList.clear()
|
||||||
|
}
|
||||||
|
segments.add(if (needUnboxing) e.expression.unboxInlineClassIfNeeded() else e.expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
is IrExpression -> {
|
||||||
|
currentList.add(if (needUnboxing) e.unboxInlineClassIfNeeded() else e)
|
||||||
}
|
}
|
||||||
segments.add(e.expression)
|
|
||||||
} else {
|
|
||||||
// IrVarargElement is either IrSpreadElement or IrExpression
|
|
||||||
currentList.add(e as IrExpression)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!currentList.isEmpty()) {
|
if (!currentList.isEmpty()) {
|
||||||
segments.add(currentList.toArrayLiteral(expression.type, expression.varargElementType))
|
segments.add(currentList.toArrayLiteral(primitiveExpressionType, primitiveElementType))
|
||||||
currentList.clear()
|
currentList.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty vararg => empty array literal
|
// empty vararg => empty array literal
|
||||||
if (segments.isEmpty()) {
|
if (segments.isEmpty()) {
|
||||||
return emptyList().toArrayLiteral(expression.type, expression.varargElementType)
|
return emptyList().toArrayLiteral(primitiveExpressionType, primitiveElementType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// vararg with a single segment => no need to concatenate
|
// vararg with a single segment => no need to concatenate
|
||||||
@@ -85,11 +126,19 @@ private class VarargTransformer(
|
|||||||
putValueArgument(0, segments.first())
|
putValueArgument(0, segments.first())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
segments.first()
|
val res = segments.first()
|
||||||
|
return if (needUnboxing)
|
||||||
|
res.boxInlineClassIfNeeded(arrayInlineClass!!)
|
||||||
|
else
|
||||||
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val arrayLiteral = segments.toArrayLiteral(IrSimpleTypeImpl(context.intrinsics.array, false, emptyList(), emptyList()), context.irBuiltIns.anyType)
|
val arrayLiteral =
|
||||||
|
segments.toArrayLiteral(
|
||||||
|
IrSimpleTypeImpl(context.intrinsics.array, false, emptyList(), emptyList()),
|
||||||
|
context.irBuiltIns.anyType
|
||||||
|
)
|
||||||
|
|
||||||
val concatFun = if (expression.type.classifierOrNull in context.intrinsics.primitiveArrays.keys) {
|
val concatFun = if (expression.type.classifierOrNull in context.intrinsics.primitiveArrays.keys) {
|
||||||
context.intrinsics.primitiveArrayConcat
|
context.intrinsics.primitiveArrayConcat
|
||||||
@@ -97,7 +146,7 @@ private class VarargTransformer(
|
|||||||
context.intrinsics.arrayConcat
|
context.intrinsics.arrayConcat
|
||||||
}
|
}
|
||||||
|
|
||||||
return IrCallImpl(
|
val res = IrCallImpl(
|
||||||
expression.startOffset,
|
expression.startOffset,
|
||||||
expression.endOffset,
|
expression.endOffset,
|
||||||
expression.type,
|
expression.type,
|
||||||
@@ -105,6 +154,11 @@ private class VarargTransformer(
|
|||||||
).apply {
|
).apply {
|
||||||
putValueArgument(0, arrayLiteral)
|
putValueArgument(0, arrayLiteral)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return if (needUnboxing)
|
||||||
|
res.boxInlineClassIfNeeded(arrayInlineClass!!)
|
||||||
|
else
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
|
|||||||
+1
-3
@@ -66,14 +66,12 @@ class NumberConversionCallsTransformer(context: JsIrBackendContext) : CallsTrans
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (type in arrayOf(irBuiltIns.byteType, irBuiltIns.shortType, irBuiltIns.intType)) {
|
for (type in arrayOf(irBuiltIns.byteType, irBuiltIns.shortType, irBuiltIns.intType)) {
|
||||||
add(type, ConversionNames.TO_CHAR, intrinsics.charConstructor)
|
add(type, ConversionNames.TO_CHAR, intrinsics.jsNumberToChar)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (type in arrayOf(irBuiltIns.floatType, irBuiltIns.doubleType, irBuiltIns.numberType)) {
|
for (type in arrayOf(irBuiltIns.floatType, irBuiltIns.doubleType, irBuiltIns.numberType)) {
|
||||||
add(type, ConversionNames.TO_CHAR, intrinsics.jsNumberToChar)
|
add(type, ConversionNames.TO_CHAR, intrinsics.jsNumberToChar)
|
||||||
}
|
}
|
||||||
|
|
||||||
add(irBuiltIns.charType, ConversionNames.TO_CHAR, ::useDispatchReceiver)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transformCall(call: IrCall): IrExpression {
|
override fun transformCall(call: IrCall): IrExpression {
|
||||||
|
|||||||
+1
@@ -1,4 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
// KJS_WITH_FULL_RUNTIME
|
||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
// IGNORE_BACKEND: JS_IR
|
// IGNORE_BACKEND: JS_IR
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ internal fun booleanArray(size: Int): BooleanArray = withType("BooleanArray", fi
|
|||||||
|
|
||||||
internal fun booleanArrayOf(arr: Array<Boolean>): BooleanArray = withType("BooleanArray", arr.asDynamic().slice()).unsafeCast<BooleanArray>()
|
internal fun booleanArrayOf(arr: Array<Boolean>): BooleanArray = withType("BooleanArray", arr.asDynamic().slice()).unsafeCast<BooleanArray>()
|
||||||
|
|
||||||
internal fun charArray(size: Int): CharArray = withType("CharArray", fillArrayVal(Array<Char>(size), '\u0000')).unsafeCast<CharArray>()
|
internal fun charArray(size: Int): CharArray = withType("CharArray", fillArrayVal(Array<Int>(size), 0)).unsafeCast<CharArray>()
|
||||||
|
|
||||||
internal fun charArrayOf(arr: Array<Char>): CharArray = withType("CharArray", arr.asDynamic().slice()).unsafeCast<CharArray>()
|
internal fun charArrayOf(arr: Array<Char>): CharArray = withType("CharArray", arr.asDynamic().slice()).unsafeCast<CharArray>()
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ package kotlin
|
|||||||
* Represents a 16-bit Unicode character.
|
* Represents a 16-bit Unicode character.
|
||||||
* On the JVM, non-nullable values of this type are represented as values of the primitive type `char`.
|
* On the JVM, non-nullable values of this type are represented as values of the primitive type `char`.
|
||||||
*/
|
*/
|
||||||
public class Char(value: Int) : Comparable<Char> {
|
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
|
||||||
|
public inline class Char internal constructor (val value: Int) : Comparable<Char> {
|
||||||
private val value = value and 0xFFFF
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares this value with the specified value for order.
|
* 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,
|
* Returns zero if this value is equal to the specified other value, a negative number if it's less than other,
|
||||||
@@ -21,17 +20,17 @@ public class Char(value: Int) : Comparable<Char> {
|
|||||||
public override fun compareTo(other: Char): Int = value - other.value
|
public override fun compareTo(other: Char): Int = value - other.value
|
||||||
|
|
||||||
/** Adds the other Int value to this value resulting a Char. */
|
/** Adds the other Int value to this value resulting a Char. */
|
||||||
public operator fun plus(other: Int): Char = Char(value + other)
|
public operator fun plus(other: Int): Char = (value + other).toChar()
|
||||||
|
|
||||||
/** Subtracts the other Char value from this value resulting an Int. */
|
/** Subtracts the other Char value from this value resulting an Int. */
|
||||||
public operator fun minus(other: Char): Int = value - other.value
|
public operator fun minus(other: Char): Int = value - other.value
|
||||||
/** Subtracts the other Int value from this value resulting a Char. */
|
/** Subtracts the other Int value from this value resulting a Char. */
|
||||||
public operator fun minus(other: Int): Char = Char(value - other)
|
public operator fun minus(other: Int): Char = (value - other).toChar()
|
||||||
|
|
||||||
/** Increments this value. */
|
/** Increments this value. */
|
||||||
public operator fun inc(): Char = Char(value + 1)
|
public operator fun inc(): Char = (value + 1).toChar()
|
||||||
/** Decrements this value. */
|
/** Decrements this value. */
|
||||||
public operator fun dec(): Char = Char(value - 1)
|
public operator fun dec(): Char = (value - 1).toChar()
|
||||||
|
|
||||||
/** Creates a range from this value to the specified [other] value. */
|
/** Creates a range from this value to the specified [other] value. */
|
||||||
public operator fun rangeTo(other: Char): CharRange = CharRange(this, other)
|
public operator fun rangeTo(other: Char): CharRange = CharRange(this, other)
|
||||||
@@ -51,10 +50,6 @@ public class Char(value: Int) : Comparable<Char> {
|
|||||||
/** Returns the value of this character as a `Double`. */
|
/** Returns the value of this character as a `Double`. */
|
||||||
public fun toDouble(): Double = value.toDouble()
|
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 {
|
override fun toString(): String {
|
||||||
val value = value
|
val value = value
|
||||||
return js("String.fromCharCode(value)").unsafeCast<String>()
|
return js("String.fromCharCode(value)").unsafeCast<String>()
|
||||||
|
|||||||
@@ -27,4 +27,4 @@ fun doubleToInt(a: dynamic) = js("""
|
|||||||
return a | 0;
|
return a | 0;
|
||||||
""").unsafeCast<Int>()
|
""").unsafeCast<Int>()
|
||||||
|
|
||||||
fun numberToChar(a: dynamic) = Char(numberToInt(a))
|
fun numberToChar(a: dynamic) = Char(numberToInt(a) and 0xFFFF)
|
||||||
Reference in New Issue
Block a user