[JS IR BE] Fix basic reflection for primitive types
This commit is contained in:
@@ -250,6 +250,9 @@ class JsIrBackendContext(
|
|||||||
).owner
|
).owner
|
||||||
|
|
||||||
val primitiveClassProperties = primitiveClassesObject.declarations.filterIsInstance<IrProperty>()
|
val primitiveClassProperties = primitiveClassesObject.declarations.filterIsInstance<IrProperty>()
|
||||||
|
val primitiveClassFunctionClass = primitiveClassesObject.declarations
|
||||||
|
.filterIsInstance<IrSimpleFunction>()
|
||||||
|
.find { it.name == Name.identifier("functionClass") }!!
|
||||||
|
|
||||||
val throwableClass = symbolTable.referenceClass(
|
val throwableClass = symbolTable.referenceClass(
|
||||||
getClass(JsIrBackendContext.KOTLIN_PACKAGE_FQN.child(Name.identifier("Throwable")))
|
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.backend.js.ir.JsIrBuilder
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
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.IrClassReference
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetClass
|
import org.jetbrains.kotlin.ir.expressions.IrGetClass
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
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.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
@@ -26,29 +29,90 @@ class ClassReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass
|
|||||||
|
|
||||||
private val primitiveClassProperties = context.primitiveClassProperties
|
private val primitiveClassProperties = context.primitiveClassProperties
|
||||||
|
|
||||||
private val booleanClass = primitiveClassProperties.single { it.name == Name.identifier("booleanClass") }
|
private fun primitiveClassProperty(name: String) =
|
||||||
private val intClass = primitiveClassProperties.single { it.name == Name.identifier("intClass") }
|
primitiveClassProperties.single { it.name == Name.identifier(name) }
|
||||||
private val doubleClass = primitiveClassProperties.single { it.name == Name.identifier("doubleClass") }
|
|
||||||
|
|
||||||
private fun callGetKClassFromExpression(returnType: IrType, typeArgument: IrType, argument: IrExpression) =
|
|
||||||
JsIrBuilder.buildCall(intrinsics.jsGetKClassFromExpression, returnType, listOf(typeArgument)).apply {
|
private val finalPrimitiveClasses = mapOf(
|
||||||
putValueArgument(0, argument)
|
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) =
|
private fun getPrimitiveClass(target: IrSimpleFunction, returnType: IrType) =
|
||||||
JsIrBuilder.buildCall(target.symbol, returnType).apply {
|
JsIrBuilder.buildCall(target.symbol, returnType).apply {
|
||||||
dispatchReceiver = JsIrBuilder.buildGetObjectValue(primitiveClassesObject.defaultType, primitiveClassesObject.symbol)
|
dispatchReceiver = JsIrBuilder.buildGetObjectValue(primitiveClassesObject.defaultType, primitiveClassesObject.symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun callGetKClass(returnType: IrType, typeArgument: IrType) = when {
|
private fun getFinalPrimitiveKClass(returnType: IrType, typeArgument: IrType): IrCall? {
|
||||||
typeArgument.isBoolean() -> getPrimitiveClass(booleanClass.getter!!, returnType)
|
for ((typePredicate, v) in finalPrimitiveClasses) {
|
||||||
typeArgument.isByte() -> getPrimitiveClass(intClass.getter!!, returnType)
|
if (typePredicate(typeArgument))
|
||||||
typeArgument.isShort() -> getPrimitiveClass(intClass.getter!!, returnType)
|
return getPrimitiveClass(v, returnType)
|
||||||
typeArgument.isInt() -> getPrimitiveClass(intClass.getter!!, returnType)
|
}
|
||||||
typeArgument.isFloat() -> getPrimitiveClass(doubleClass.getter!!, returnType)
|
|
||||||
typeArgument.isDouble() -> getPrimitiveClass(doubleClass.getter!!, returnType)
|
return null
|
||||||
else -> JsIrBuilder.buildCall(intrinsics.jsGetKClass, returnType, listOf(typeArgument)).apply {
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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))
|
putValueArgument(0, callJsClass(typeArgument))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-3
@@ -38,9 +38,16 @@ private class VarargTransformer(
|
|||||||
) : IrElementTransformerVoid() {
|
) : IrElementTransformerVoid() {
|
||||||
|
|
||||||
private fun List<IrExpression>.toArrayLiteral(type: IrType, varargElementType: IrType): IrExpression {
|
private fun List<IrExpression>.toArrayLiteral(type: IrType, varargElementType: IrType): IrExpression {
|
||||||
val intrinsic = context.intrinsics.primitiveArrays[type.classifierOrNull]?.let { primitiveType ->
|
|
||||||
context.intrinsics.primitiveToLiteralConstructor[primitiveType]
|
// TODO: Use symbols when builtins symbol table is fixes
|
||||||
} ?: context.intrinsics.arrayLiteral
|
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 startOffset = firstOrNull()?.startOffset ?: UNDEFINED_OFFSET
|
||||||
val endOffset = lastOrNull()?.endOffset ?: 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.isComparable(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.comparable.toUnsafe())
|
||||||
fun IrType.isCharSequence(): Boolean = isNotNullClassType(KotlinBuiltIns.FQ_NAMES.charSequence)
|
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.isNullableBoolean(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES._boolean)
|
||||||
fun IrType.isNullableLong(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES._long)
|
fun IrType.isNullableLong(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES._long)
|
||||||
fun IrType.isNullableChar(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES._char)
|
fun IrType.isNullableChar(): Boolean = isNullableClassType(KotlinBuiltIns.FQ_NAMES._char)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1233
|
// EXPECTED_REACHABLE_NODES: 1233
|
||||||
// LANGUAGE_VERSION: 1.2
|
// LANGUAGE_VERSION: 1.2
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@@ -58,8 +57,13 @@ fun box(): String {
|
|||||||
|
|
||||||
assertEquals("Long", Long::class.simpleName)
|
assertEquals("Long", Long::class.simpleName)
|
||||||
assertEquals("Long", 23L::class.simpleName)
|
assertEquals("Long", 23L::class.simpleName)
|
||||||
|
if (testUtils.isLegacyBackend()) {
|
||||||
assertEquals("BoxedChar", Char::class.simpleName)
|
assertEquals("BoxedChar", Char::class.simpleName)
|
||||||
assertEquals("BoxedChar", '@'::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("RuntimeException", RuntimeException()::class.simpleName)
|
assertEquals("RuntimeException", RuntimeException()::class.simpleName)
|
||||||
assertEquals("KClass", KClass::class.simpleName)
|
assertEquals("KClass", KClass::class.simpleName)
|
||||||
|
|||||||
+5
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JS_IR
|
|
||||||
// EXPECTED_REACHABLE_NODES: 1290
|
// EXPECTED_REACHABLE_NODES: 1290
|
||||||
// LANGUAGE_VERSION: 1.1
|
// LANGUAGE_VERSION: 1.1
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
@@ -32,8 +31,13 @@ fun box(): String {
|
|||||||
|
|
||||||
assertEquals("Long", Long::class.simpleName)
|
assertEquals("Long", Long::class.simpleName)
|
||||||
assertEquals("Long", 23L::class.simpleName)
|
assertEquals("Long", 23L::class.simpleName)
|
||||||
|
if (testUtils.isLegacyBackend()) {
|
||||||
assertEquals("BoxedChar", Char::class.simpleName)
|
assertEquals("BoxedChar", Char::class.simpleName)
|
||||||
assertEquals("BoxedChar", '@'::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("RuntimeException", RuntimeException()::class.simpleName)
|
assertEquals("RuntimeException", RuntimeException()::class.simpleName)
|
||||||
assertEquals("KClass", KClass::class.simpleName)
|
assertEquals("KClass", KClass::class.simpleName)
|
||||||
|
|||||||
Reference in New Issue
Block a user