diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index ba8f5478c2b..ad59a66ccb9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -250,6 +250,9 @@ class JsIrBackendContext( ).owner val primitiveClassProperties = primitiveClassesObject.declarations.filterIsInstance() + val primitiveClassFunctionClass = primitiveClassesObject.declarations + .filterIsInstance() + .find { it.name == Name.identifier("functionClass") }!! val throwableClass = symbolTable.referenceClass( getClass(JsIrBackendContext.KOTLIN_PACKAGE_FQN.child(Name.identifier("Throwable"))) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ClassReferenceLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ClassReferenceLowering.kt index b74badeee68..97e09ac11a8 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ClassReferenceLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ClassReferenceLowering.kt @@ -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)) } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt index 9675fddde10..cdf7a9cb8ef 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/VarargLowering.kt @@ -38,9 +38,16 @@ private class VarargTransformer( ) : IrElementTransformerVoid() { private fun List.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 diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt index 4efb0d05814..827ce74b8ff 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypePredicates.kt @@ -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) diff --git a/js/js.translator/testData/box/reflection/primitives-12.kt b/js/js.translator/testData/box/reflection/primitives-12.kt index 5bb0d7099dc..94e507f0258 100644 --- a/js/js.translator/testData/box/reflection/primitives-12.kt +++ b/js/js.translator/testData/box/reflection/primitives-12.kt @@ -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) diff --git a/js/js.translator/testData/box/reflection/primitives.kt b/js/js.translator/testData/box/reflection/primitives.kt index a49355d3732..f16efce7fe2 100644 --- a/js/js.translator/testData/box/reflection/primitives.kt +++ b/js/js.translator/testData/box/reflection/primitives.kt @@ -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)