[JS IR BE] Fix basic reflection for primitive types
This commit is contained in:
@@ -250,6 +250,9 @@ class JsIrBackendContext(
|
||||
).owner
|
||||
|
||||
val primitiveClassProperties = primitiveClassesObject.declarations.filterIsInstance<IrProperty>()
|
||||
val primitiveClassFunctionClass = primitiveClassesObject.declarations
|
||||
.filterIsInstance<IrSimpleFunction>()
|
||||
.find { it.name == Name.identifier("functionClass") }!!
|
||||
|
||||
val throwableClass = symbolTable.referenceClass(
|
||||
getClass(JsIrBackendContext.KOTLIN_PACKAGE_FQN.child(Name.identifier("Throwable")))
|
||||
|
||||
+78
-14
@@ -10,11 +10,14 @@ import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrClassReference
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetClass
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.isFunction
|
||||
import org.jetbrains.kotlin.ir.util.isThrowable
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -26,29 +29,90 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass
|
||||
|
||||
private val primitiveClassProperties = context.primitiveClassProperties
|
||||
|
||||
private val booleanClass = primitiveClassProperties.single { it.name == Name.identifier("booleanClass") }
|
||||
private val intClass = primitiveClassProperties.single { it.name == Name.identifier("intClass") }
|
||||
private val doubleClass = primitiveClassProperties.single { it.name == Name.identifier("doubleClass") }
|
||||
private fun primitiveClassProperty(name: String) =
|
||||
primitiveClassProperties.single { it.name == Name.identifier(name) }
|
||||
|
||||
private fun callGetKClassFromExpression(returnType: IrType, typeArgument: IrType, argument: IrExpression) =
|
||||
JsIrBuilder.buildCall(intrinsics.jsGetKClassFromExpression, returnType, listOf(typeArgument)).apply {
|
||||
|
||||
private val finalPrimitiveClasses = mapOf(
|
||||
IrType::isBoolean to "booleanClass",
|
||||
IrType::isByte to "byteClass",
|
||||
IrType::isShort to "shortClass",
|
||||
IrType::isInt to "intClass",
|
||||
IrType::isFloat to "floatClass",
|
||||
IrType::isDouble to "doubleClass",
|
||||
IrType::isArray to "arrayClass",
|
||||
IrType::isString to "stringClass",
|
||||
IrType::isThrowable to "throwableClass",
|
||||
IrType::isBooleanArray to "booleanArrayClass",
|
||||
IrType::isCharArray to "charArrayClass",
|
||||
IrType::isByteArray to "byteArrayClass",
|
||||
IrType::isShortArray to "shortArrayClass",
|
||||
IrType::isIntArray to "intArrayClass",
|
||||
IrType::isLongArray to "longArrayClass",
|
||||
IrType::isFloatArray to "floatArrayClass",
|
||||
IrType::isDoubleArray to "doubleArrayClass"
|
||||
).mapValues {
|
||||
primitiveClassProperty(it.value).getter!!
|
||||
}
|
||||
|
||||
private val openPrimitiveClasses = mapOf(
|
||||
IrType::isAny to "anyClass",
|
||||
IrType::isNumber to "numberClass",
|
||||
IrType::isNothing to "nothingClass"
|
||||
).mapValues {
|
||||
primitiveClassProperty(it.value).getter!!
|
||||
}
|
||||
|
||||
private fun callGetKClassFromExpression(returnType: IrType, typeArgument: IrType, argument: IrExpression): IrExpression {
|
||||
val primitiveKClass = getFinalPrimitiveKClass(returnType, typeArgument)
|
||||
if (primitiveKClass != null)
|
||||
return JsIrBuilder.buildBlock(returnType, listOf(argument, primitiveKClass))
|
||||
|
||||
return JsIrBuilder.buildCall(intrinsics.jsGetKClassFromExpression, returnType, listOf(typeArgument)).apply {
|
||||
putValueArgument(0, argument)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun getPrimitiveClass(target: IrSimpleFunction, returnType: IrType) =
|
||||
JsIrBuilder.buildCall(target.symbol, returnType).apply {
|
||||
dispatchReceiver = JsIrBuilder.buildGetObjectValue(primitiveClassesObject.defaultType, primitiveClassesObject.symbol)
|
||||
}
|
||||
|
||||
private fun callGetKClass(returnType: IrType, typeArgument: IrType) = when {
|
||||
typeArgument.isBoolean() -> getPrimitiveClass(booleanClass.getter!!, returnType)
|
||||
typeArgument.isByte() -> getPrimitiveClass(intClass.getter!!, returnType)
|
||||
typeArgument.isShort() -> getPrimitiveClass(intClass.getter!!, returnType)
|
||||
typeArgument.isInt() -> getPrimitiveClass(intClass.getter!!, returnType)
|
||||
typeArgument.isFloat() -> getPrimitiveClass(doubleClass.getter!!, returnType)
|
||||
typeArgument.isDouble() -> getPrimitiveClass(doubleClass.getter!!, returnType)
|
||||
else -> JsIrBuilder.buildCall(intrinsics.jsGetKClass, returnType, listOf(typeArgument)).apply {
|
||||
private fun getFinalPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
|
||||
for ((typePredicate, v) in finalPrimitiveClasses) {
|
||||
if (typePredicate(typeArgument))
|
||||
return getPrimitiveClass(v, returnType)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
private fun getOpenPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
|
||||
for ((typePredicate, v) in openPrimitiveClasses) {
|
||||
if (typePredicate(typeArgument))
|
||||
return getPrimitiveClass(v, returnType)
|
||||
}
|
||||
|
||||
if (typeArgument.isFunction()) {
|
||||
val functionInterface = typeArgument.getClass()!!
|
||||
val arity = functionInterface.typeParameters.size - 1
|
||||
return getPrimitiveClass(context.primitiveClassFunctionClass, returnType).apply {
|
||||
putValueArgument(0, JsIrBuilder.buildInt(context.irBuiltIns.intType, arity))
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun callGetKClass(returnType: IrType, typeArgument: IrType): IrCall {
|
||||
val primitiveKClass =
|
||||
getFinalPrimitiveKClass(returnType, typeArgument) ?: getOpenPrimitiveKClass(returnType, typeArgument)
|
||||
|
||||
if (primitiveKClass != null)
|
||||
return primitiveKClass
|
||||
|
||||
return JsIrBuilder.buildCall(intrinsics.jsGetKClass, returnType, listOf(typeArgument)).apply {
|
||||
putValueArgument(0, callJsClass(typeArgument))
|
||||
}
|
||||
}
|
||||
|
||||
+10
-3
@@ -38,9 +38,16 @@ private class VarargTransformer(
|
||||
) : IrElementTransformerVoid() {
|
||||
|
||||
private fun List<IrExpression>.toArrayLiteral(type: IrType, varargElementType: IrType): IrExpression {
|
||||
val intrinsic = context.intrinsics.primitiveArrays[type.classifierOrNull]?.let { primitiveType ->
|
||||
context.intrinsics.primitiveToLiteralConstructor[primitiveType]
|
||||
} ?: context.intrinsics.arrayLiteral
|
||||
|
||||
// TODO: Use symbols when builtins symbol table is fixes
|
||||
val primitiveType = context.intrinsics.primitiveArrays
|
||||
.mapKeys { it.key.descriptor }[type.classifierOrNull?.descriptor]
|
||||
|
||||
val intrinsic =
|
||||
if (primitiveType != null)
|
||||
context.intrinsics.primitiveToLiteralConstructor.getValue(primitiveType)
|
||||
else
|
||||
context.intrinsics.arrayLiteral
|
||||
|
||||
val startOffset = firstOrNull()?.startOffset ?: UNDEFINED_OFFSET
|
||||
val endOffset = lastOrNull()?.endOffset ?: UNDEFINED_OFFSET
|
||||
|
||||
@@ -64,6 +64,15 @@ fun IrType.isNumber(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.numb
|
||||
fun IrType.isComparable(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.comparable.toUnsafe())
|
||||
fun IrType.isCharSequence(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.charSequence)
|
||||
|
||||
fun IrType.isBooleanArray(): Boolean = isNotNullClassType(FqNameUnsafe("kotlin.BooleanArray"))
|
||||
fun IrType.isCharArray(): Boolean = isNotNullClassType(FqNameUnsafe("kotlin.CharArray"))
|
||||
fun IrType.isByteArray(): Boolean = isNotNullClassType(FqNameUnsafe("kotlin.ByteArray"))
|
||||
fun IrType.isShortArray(): Boolean = isNotNullClassType(FqNameUnsafe("kotlin.ShortArray"))
|
||||
fun IrType.isIntArray(): Boolean = isNotNullClassType(FqNameUnsafe("kotlin.IntArray"))
|
||||
fun IrType.isLongArray(): Boolean = isNotNullClassType(FqNameUnsafe("kotlin.LongArray"))
|
||||
fun IrType.isFloatArray(): Boolean = isNotNullClassType(FqNameUnsafe("kotlin.FloatArray"))
|
||||
fun IrType.isDoubleArray(): Boolean = isNotNullClassType(FqNameUnsafe("kotlin.DoubleArray"))
|
||||
|
||||
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)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1233
|
||||
// LANGUAGE_VERSION: 1.2
|
||||
import kotlin.reflect.KClass
|
||||
@@ -58,8 +57,13 @@ fun box(): String {
|
||||
|
||||
assertEquals("Long", Long::class.simpleName)
|
||||
assertEquals("Long", 23L::class.simpleName)
|
||||
assertEquals("BoxedChar", Char::class.simpleName)
|
||||
assertEquals("BoxedChar", '@'::class.simpleName)
|
||||
if (testUtils.isLegacyBackend()) {
|
||||
assertEquals("BoxedChar", Char::class.simpleName)
|
||||
assertEquals("BoxedChar", '@'::class.simpleName)
|
||||
} else {
|
||||
assertEquals("Char", Char::class.simpleName)
|
||||
assertEquals("Char", '@'::class.simpleName)
|
||||
}
|
||||
assertEquals("RuntimeException", RuntimeException::class.simpleName)
|
||||
assertEquals("RuntimeException", RuntimeException()::class.simpleName)
|
||||
assertEquals("KClass", KClass::class.simpleName)
|
||||
|
||||
+7
-3
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// EXPECTED_REACHABLE_NODES: 1290
|
||||
// LANGUAGE_VERSION: 1.1
|
||||
import kotlin.reflect.KClass
|
||||
@@ -32,8 +31,13 @@ fun box(): String {
|
||||
|
||||
assertEquals("Long", Long::class.simpleName)
|
||||
assertEquals("Long", 23L::class.simpleName)
|
||||
assertEquals("BoxedChar", Char::class.simpleName)
|
||||
assertEquals("BoxedChar", '@'::class.simpleName)
|
||||
if (testUtils.isLegacyBackend()) {
|
||||
assertEquals("BoxedChar", Char::class.simpleName)
|
||||
assertEquals("BoxedChar", '@'::class.simpleName)
|
||||
} else {
|
||||
assertEquals("Char", Char::class.simpleName)
|
||||
assertEquals("Char", '@'::class.simpleName)
|
||||
}
|
||||
assertEquals("RuntimeException", RuntimeException::class.simpleName)
|
||||
assertEquals("RuntimeException", RuntimeException()::class.simpleName)
|
||||
assertEquals("KClass", KClass::class.simpleName)
|
||||
|
||||
Reference in New Issue
Block a user