[JS IR BE] Fix basic reflection for primitive types

This commit is contained in:
Svyatoslav Kuzmich
2019-02-25 15:06:46 +03:00
parent a01407eec9
commit 8da076c1ed
6 changed files with 114 additions and 23 deletions
@@ -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")))
@@ -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))
}
}
@@ -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)
+7 -3
View File
@@ -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
View File
@@ -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)