From 723c9be5a0089a3c47f987071c1269ef30558643 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 1 Mar 2017 20:04:50 +0300 Subject: [PATCH] JS: fix class literal expression with primitive classes. See KT-16545 --- js/js.libraries/src/js/long.js | 6 +++ js/js.libraries/src/reflect/JsClass.kt | 7 ++- .../js/test/semantics/BoxJsTestGenerated.java | 6 +++ .../js/translate/context/StaticContext.java | 25 ++++++++++ .../expression/ExpressionVisitor.java | 4 ++ .../testData/box/reflection/primitives.kt | 49 +++++++++++++++++++ 6 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 js/js.translator/testData/box/reflection/primitives.kt diff --git a/js/js.libraries/src/js/long.js b/js/js.libraries/src/js/long.js index eeb761e076a..4707f6fa47c 100644 --- a/js/js.libraries/src/js/long.js +++ b/js/js.libraries/src/js/long.js @@ -64,6 +64,12 @@ Kotlin.Long = function(low, high) { this.high_ = high | 0; // force into 32 signed bits. }; +Kotlin.Long.$metadata$ = { + kind: "class", + simpleName: "Long", + interfaces:[] +}; + // NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the // from* methods on which they depend. diff --git a/js/js.libraries/src/reflect/JsClass.kt b/js/js.libraries/src/reflect/JsClass.kt index b4fa17d8f63..a595363b6ee 100644 --- a/js/js.libraries/src/reflect/JsClass.kt +++ b/js/js.libraries/src/reflect/JsClass.kt @@ -35,7 +35,12 @@ external fun jsClass(): JsClass @Deprecated("Use class literal and extension property `js` instead.", replaceWith = ReplaceWith("this::class.js"), level = DeprecationLevel.WARNING) val T.jsClass: JsClass - get() = js("Object").getPrototypeOf(this).constructor + get() = when (jsTypeOf(this)) { + "string" -> js("String") + "number" -> js("Number") + "boolean" -> js("Boolean") + else -> js("Object").getPrototypeOf(this).constructor + } /** * Obtains a constructor reference for the given `KClass`. diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 12293c88214..840dbfa0134 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -6883,6 +6883,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/reflection/kClassToAndFromJsClass.kt"); doTest(fileName); } + + @TestMetadata("primitives.kt") + public void testPrimitives() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/reflection/primitives.kt"); + doTest(fileName); + } } @TestMetadata("js/js.translator/testData/box/regression") diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index 10921ee5310..b91cac0c022 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -24,6 +24,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.LinkedHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.builtins.FunctionTypesKt; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; @@ -47,6 +48,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.serialization.js.ModuleKind; +import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; import java.util.*; @@ -247,9 +249,32 @@ public final class StaticContext { private JsExpression buildQualifiedExpression(@NotNull DeclarationDescriptor descriptor) { if (descriptor instanceof ClassDescriptor) { ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; + KotlinType type = classDescriptor.getDefaultType(); if (KotlinBuiltIns.isAny(classDescriptor)) { return pureFqn("Object", null); } + else if (KotlinBuiltIns.isInt(type) || KotlinBuiltIns.isShort(type) || KotlinBuiltIns.isByte(type) || + KotlinBuiltIns.isFloat(type) || KotlinBuiltIns.isDouble(type)) { + return pureFqn("Number", null); + } + else if (KotlinBuiltIns.isLong(type)) { + return pureFqn("Long", Namer.kotlinObject()); + } + else if (KotlinBuiltIns.isChar(type)) { + return pureFqn("BoxedChar", Namer.kotlinObject()); + } + else if (KotlinBuiltIns.isString(type)) { + return pureFqn("String", null); + } + else if (KotlinBuiltIns.isBoolean(type)) { + return pureFqn("Boolean", null); + } + else if (KotlinBuiltIns.isArrayOrPrimitiveArray(classDescriptor)) { + return pureFqn("Array", null); + } + else if (FunctionTypesKt.isBuiltinFunctionalType(type)) { + return pureFqn("Function", null); + } else if (TypeUtilsKt.isThrowable(classDescriptor.getDefaultType())) { return pureFqn("Error", null); } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java index f2736bf4043..f7f3705cf38 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java @@ -238,6 +238,10 @@ public final class ExpressionVisitor extends TranslatorVisitor { if (lhs instanceof DoubleColonLHS.Expression && !((DoubleColonLHS.Expression) lhs).isObjectQualifier()) { JsExpression receiver = translateAsExpression(receiverExpression, context); + KotlinType type = context.bindingContext().getType(receiverExpression); + if (type != null && KotlinBuiltIns.isChar(type)) { + receiver = JsAstUtils.charToBoxedChar(receiver); + } return new JsInvocation(context.namer().kotlin(GET_KCLASS_FROM_EXPRESSION), receiver); } diff --git a/js/js.translator/testData/box/reflection/primitives.kt b/js/js.translator/testData/box/reflection/primitives.kt new file mode 100644 index 00000000000..48790a89f6c --- /dev/null +++ b/js/js.translator/testData/box/reflection/primitives.kt @@ -0,0 +1,49 @@ +import kotlin.reflect.KClass + +fun box(): String { + check(js("Object"), Any::class) + check(js("String"), String::class) + check(js("Boolean"), Boolean::class) + check(js("Error"), Throwable::class) + check(js("Array"), Array::class) + check(js("Function"), Function0::class) + + check(js("Number"), Byte::class) + check(js("Number"), Short::class) + check(js("Number"), Int::class) + check(js("Number"), Float::class) + check(js("Number"), Double::class) + + check(js("Object"), Any()) + check(js("String"), "*") + check(js("Boolean"), true) + check(js("Error"), Throwable()) + check(js("Array"), arrayOf(1, 2, 3)) + check(js("Function"), { x: Int -> x }) + + check(js("Number"), 23.toByte()) + check(js("Number"), 23.toShort()) + check(js("Number"), 23) + check(js("Number"), 23.0F) + check(js("Number"), 23.0) + + assertEquals("Long", Long::class.simpleName) + assertEquals("Long", 23L::class.simpleName) + assertEquals("BoxedChar", Char::class.simpleName) + assertEquals("BoxedChar", '@'::class.simpleName) + assertEquals("RuntimeException", RuntimeException::class.simpleName) + assertEquals("RuntimeException", RuntimeException()::class.simpleName) + assertEquals("KClass", KClass::class.simpleName) + assertEquals("KClassImpl", Any::class::class.simpleName) + + return "OK" +} + +private fun check(nativeClass: dynamic, c: KClass<*>) { + assertEquals(null, c.simpleName, "Simple name of native class ${nativeClass.name} must be null") + assertEquals(nativeClass, c.js, "Kotlin class does not correspond native class ${nativeClass.name}") +} + +private fun check(nativeClass: dynamic, value: Any) { + check(nativeClass, value::class) +} \ No newline at end of file