[JS IR BE] Covert Char into inline class

This commit is contained in:
Svyatoslav Kuzmich
2019-01-24 18:45:50 +03:00
parent a5f537adc5
commit 2818d3767e
9 changed files with 90 additions and 39 deletions
@@ -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
)
)
@@ -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)
@@ -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
@@ -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 {
@@ -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,4 +1,5 @@
// WITH_RUNTIME
// KJS_WITH_FULL_RUNTIME
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
+1 -1
View File
@@ -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>()
+7 -12
View File
@@ -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)