[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.util.deepCopyWithSymbols
|
||||
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) }
|
||||
|
||||
@@ -371,13 +370,13 @@ val jsPhases = listOf(
|
||||
TypeOperatorLoweringPhase,
|
||||
SecondaryConstructorLoweringPhase,
|
||||
SecondaryFactoryInjectorLoweringPhase,
|
||||
ClassReferenceLoweringPhase,
|
||||
InlineClassLoweringPhase,
|
||||
AutoboxingTransformerPhase,
|
||||
BlockDecomposerLoweringPhase,
|
||||
ClassReferenceLoweringPhase,
|
||||
PrimitiveCompanionLoweringPhase,
|
||||
ConstLoweringPhase,
|
||||
CallsLoweringPhase,
|
||||
IrModuleEndPhase,
|
||||
IrToJsPhase
|
||||
)
|
||||
)
|
||||
|
||||
+7
-4
@@ -6,14 +6,15 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower
|
||||
|
||||
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.util.irCall
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
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.transformChildrenVoid
|
||||
|
||||
@@ -29,9 +30,11 @@ private class ArrayConstructorTransformer(
|
||||
val context: JsIrBackendContext
|
||||
) : IrElementTransformerVoid() {
|
||||
|
||||
private val primitiveArrayInlineToSizeConstructorMap = context.intrinsics.primitiveArrays.keys.associate {
|
||||
it.inlineConstructor to it.sizeConstructor
|
||||
}
|
||||
// Inline constructor for CharArray is implemented in runtime
|
||||
private val primitiveArrayInlineToSizeConstructorMap =
|
||||
context.intrinsics.primitiveArrays.filter { it.value != PrimitiveType.CHAR }.keys.associate {
|
||||
it.inlineConstructor to it.sizeConstructor
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
+2
-1
@@ -151,7 +151,8 @@ class AutoboxingTransformer(val context: JsIrBackendContext) : AbstractValueUsag
|
||||
|
||||
override fun IrExpression.useAsVarargElement(expression: IrVararg): IrExpression {
|
||||
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
|
||||
else
|
||||
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.ir.UNDEFINED_OFFSET
|
||||
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.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.IrGetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
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.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 {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
val currentList = 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) {
|
||||
if (e is IrSpreadElement) {
|
||||
if (!currentList.isEmpty()) {
|
||||
segments.add(currentList.toArrayLiteral(expression.type, expression.varargElementType))
|
||||
currentList.clear()
|
||||
when (e) {
|
||||
is IrSpreadElement -> {
|
||||
if (!currentList.isEmpty()) {
|
||||
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()) {
|
||||
segments.add(currentList.toArrayLiteral(expression.type, expression.varargElementType))
|
||||
segments.add(currentList.toArrayLiteral(primitiveExpressionType, primitiveElementType))
|
||||
currentList.clear()
|
||||
}
|
||||
|
||||
// empty vararg => empty array literal
|
||||
if (segments.isEmpty()) {
|
||||
return emptyList().toArrayLiteral(expression.type, expression.varargElementType)
|
||||
return emptyList().toArrayLiteral(primitiveExpressionType, primitiveElementType)
|
||||
}
|
||||
|
||||
// vararg with a single segment => no need to concatenate
|
||||
@@ -85,11 +126,19 @@ private class VarargTransformer(
|
||||
putValueArgument(0, segments.first())
|
||||
}
|
||||
} 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) {
|
||||
context.intrinsics.primitiveArrayConcat
|
||||
@@ -97,7 +146,7 @@ private class VarargTransformer(
|
||||
context.intrinsics.arrayConcat
|
||||
}
|
||||
|
||||
return IrCallImpl(
|
||||
val res = IrCallImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
@@ -105,6 +154,11 @@ private class VarargTransformer(
|
||||
).apply {
|
||||
putValueArgument(0, arrayLiteral)
|
||||
}
|
||||
|
||||
return if (needUnboxing)
|
||||
res.boxInlineClassIfNeeded(arrayInlineClass!!)
|
||||
else
|
||||
res
|
||||
}
|
||||
|
||||
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)) {
|
||||
add(type, ConversionNames.TO_CHAR, intrinsics.charConstructor)
|
||||
add(type, ConversionNames.TO_CHAR, intrinsics.jsNumberToChar)
|
||||
}
|
||||
|
||||
for (type in arrayOf(irBuiltIns.floatType, irBuiltIns.doubleType, irBuiltIns.numberType)) {
|
||||
add(type, ConversionNames.TO_CHAR, intrinsics.jsNumberToChar)
|
||||
}
|
||||
|
||||
add(irBuiltIns.charType, ConversionNames.TO_CHAR, ::useDispatchReceiver)
|
||||
}
|
||||
|
||||
override fun transformCall(call: IrCall): IrExpression {
|
||||
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// KJS_WITH_FULL_RUNTIME
|
||||
// IGNORE_BACKEND: JVM_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 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>()
|
||||
|
||||
|
||||
@@ -9,10 +9,9 @@ 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<Char> {
|
||||
|
||||
private val value = value and 0xFFFF
|
||||
|
||||
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
|
||||
public inline class Char internal constructor (val value: Int) : Comparable<Char> {
|
||||
|
||||
/**
|
||||
* 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,
|
||||
@@ -21,17 +20,17 @@ public class Char(value: Int) : Comparable<Char> {
|
||||
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)
|
||||
public operator fun plus(other: Int): Char = (value + other).toChar()
|
||||
|
||||
/** 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)
|
||||
public operator fun minus(other: Int): Char = (value - other).toChar()
|
||||
|
||||
/** Increments this value. */
|
||||
public operator fun inc(): Char = Char(value + 1)
|
||||
public operator fun inc(): Char = (value + 1).toChar()
|
||||
/** 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. */
|
||||
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`. */
|
||||
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<String>()
|
||||
|
||||
@@ -27,4 +27,4 @@ fun doubleToInt(a: dynamic) = js("""
|
||||
return a | 0;
|
||||
""").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