diff --git a/compiler/testData/codegen/box/callableReference/function/genericCallableReferencesWithNullableTypes.kt b/compiler/testData/codegen/box/callableReference/function/genericCallableReferencesWithNullableTypes.kt index 5feb3593e9d..59896ac53f8 100644 --- a/compiler/testData/codegen/box/callableReference/function/genericCallableReferencesWithNullableTypes.kt +++ b/compiler/testData/codegen/box/callableReference/function/genericCallableReferencesWithNullableTypes.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JS - // WITH_RUNTIME // WITH_REFLECT diff --git a/compiler/testData/codegen/box/callableReference/function/genericCallableReferencesWithOverload.kt b/compiler/testData/codegen/box/callableReference/function/genericCallableReferencesWithOverload.kt index cb7ab6e616f..874f41cac3c 100644 --- a/compiler/testData/codegen/box/callableReference/function/genericCallableReferencesWithOverload.kt +++ b/compiler/testData/codegen/box/callableReference/function/genericCallableReferencesWithOverload.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JS - // WITH_RUNTIME // WITH_REFLECT diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/function/genericCallableReferencesWithNullableTypes.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/function/genericCallableReferencesWithNullableTypes.kt index f68812dc4a5..8ca91f5e47b 100644 --- a/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/function/genericCallableReferencesWithNullableTypes.kt +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/callableReference/function/genericCallableReferencesWithNullableTypes.kt @@ -1,4 +1,4 @@ -// IGNORE_BACKEND: JS, NATIVE +// IGNORE_BACKEND: NATIVE // IGNORE_BACKEND: JVM_IR // WITH_REFLECT // WITH_COROUTINES diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt index d5dd0e1d76a..2a88307fa3d 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt @@ -127,6 +127,8 @@ var JsFunction.coroutineMetadata: CoroutineMetadata? by MetadataProperty(default var JsExpression.range: Pair? by MetadataProperty(default = null) +var JsExpression.primitiveKClass: JsExpression? by MetadataProperty(default = null) + data class CoroutineMetadata( val doResumeName: JsName, val stateName: JsName, @@ -163,7 +165,8 @@ enum class SpecialFunction(val suggestedName: String) { COROUTINE_RESULT("coroutineResult"), COROUTINE_CONTROLLER("coroutineController"), COROUTINE_RECEIVER("coroutineReceiver"), - SET_COROUTINE_RESULT("setCoroutineResult") + SET_COROUTINE_RESULT("setCoroutineResult"), + GET_KCLASS("getKClass") } enum class BoxingKind { diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/InliningScope.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/InliningScope.kt index 63410237691..429036fca65 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/InliningScope.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/InliningScope.kt @@ -8,10 +8,7 @@ package org.jetbrains.kotlin.js.inline import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.metadata.localAlias import org.jetbrains.kotlin.js.backend.ast.metadata.staticRef -import org.jetbrains.kotlin.js.inline.clean.removeUnusedFunctionDefinitions -import org.jetbrains.kotlin.js.inline.clean.removeUnusedImports -import org.jetbrains.kotlin.js.inline.clean.renameLabels -import org.jetbrains.kotlin.js.inline.clean.simplifyWrappedFunctions +import org.jetbrains.kotlin.js.inline.clean.* import org.jetbrains.kotlin.js.inline.util.* import org.jetbrains.kotlin.js.translate.declaration.transformSpecialFunctionsToCoroutineMetadata import org.jetbrains.kotlin.js.translate.utils.JsAstUtils @@ -232,6 +229,7 @@ class ImportIntoFragmentInliningScope private constructor( InlineSuspendFunctionSplitter(this).accept(allCode) simplifyWrappedFunctions(allCode) + emergePrimitiveKClass(allCode) removeUnusedFunctionDefinitions(allCode, collectNamedFunctions(allCode)) removeUnusedImports(fragment, allCode) renameLabels(allCode) diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/emergePrimitiveKClass.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/emergePrimitiveKClass.kt new file mode 100644 index 00000000000..1dcecbe7ec4 --- /dev/null +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/emergePrimitiveKClass.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.js.inline.clean + +import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction +import org.jetbrains.kotlin.js.backend.ast.metadata.primitiveKClass +import org.jetbrains.kotlin.js.backend.ast.metadata.specialFunction + +// Replaces getKClass() with PrimitiveClasses. +fun emergePrimitiveKClass(root: JsNode) { + val visitor = object : JsVisitorWithContextImpl() { + override fun endVisit(invocation: JsInvocation, ctx: JsContext) { + val qualifier = invocation.qualifier as? JsNameRef ?: return + if (qualifier.name?.specialFunction != SpecialFunction.GET_KCLASS) return + + val firstArg = invocation.arguments.firstOrNull() as? JsNameRef ?: return + firstArg.primitiveKClass?.let { + ctx.replaceMe(it) + } + } + } + visitor.accept(root) +} \ No newline at end of file diff --git a/js/js.serializer/src/js-ast.proto b/js/js.serializer/src/js-ast.proto index fc6d5ea31b5..974e5bd8104 100644 --- a/js/js.serializer/src/js-ast.proto +++ b/js/js.serializer/src/js-ast.proto @@ -464,6 +464,7 @@ enum SpecialFunction { COROUTINE_CONTROLLER = 7; COROUTINE_RECEIVER = 8; SET_COROUTINE_RESULT = 9; + GET_KCLASS = 10; } diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt index ea535c21ab9..1d2497f5c0a 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstDeserializer.kt @@ -557,6 +557,7 @@ class JsAstDeserializer(program: JsProgram, private val sourceRoots: Iterable SpecialFunction.COROUTINE_CONTROLLER JsAstProtoBuf.SpecialFunction.COROUTINE_RECEIVER -> SpecialFunction.COROUTINE_RECEIVER JsAstProtoBuf.SpecialFunction.SET_COROUTINE_RESULT -> SpecialFunction.SET_COROUTINE_RESULT + JsAstProtoBuf.SpecialFunction.GET_KCLASS -> SpecialFunction.GET_KCLASS } private fun withLocation(fileId: Int?, location: Location?, action: () -> T): T { diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java index 076c00fef36..ec4739ee311 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstProtoBuf.java @@ -179,6 +179,10 @@ public final class JsAstProtoBuf { * SET_COROUTINE_RESULT = 9; */ SET_COROUTINE_RESULT(8, 9), + /** + * GET_KCLASS = 10; + */ + GET_KCLASS(9, 10), ; /** @@ -217,6 +221,10 @@ public final class JsAstProtoBuf { * SET_COROUTINE_RESULT = 9; */ public static final int SET_COROUTINE_RESULT_VALUE = 9; + /** + * GET_KCLASS = 10; + */ + public static final int GET_KCLASS_VALUE = 10; public final int getNumber() { return value; } @@ -232,6 +240,7 @@ public final class JsAstProtoBuf { case 7: return COROUTINE_CONTROLLER; case 8: return COROUTINE_RECEIVER; case 9: return SET_COROUTINE_RESULT; + case 10: return GET_KCLASS; default: return null; } } diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt index ef2d0929e57..55614607072 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/ast/JsAstSerializer.kt @@ -599,6 +599,7 @@ class JsAstSerializer(private val jsAstValidator: ((JsProgramFragment, Set JsAstProtoBuf.SpecialFunction.COROUTINE_CONTROLLER SpecialFunction.COROUTINE_RECEIVER -> JsAstProtoBuf.SpecialFunction.COROUTINE_RECEIVER SpecialFunction.SET_COROUTINE_RESULT -> JsAstProtoBuf.SpecialFunction.SET_COROUTINE_RESULT + SpecialFunction.GET_KCLASS -> JsAstProtoBuf.SpecialFunction.GET_KCLASS } private fun serialize(name: JsName): Int = nameMap.getOrPut(name) { diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java index cf78f7323f1..f6d261e4eed 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java @@ -6703,6 +6703,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/reflection/kClassOnReifiedTypeInLambda.kt"); } + @TestMetadata("kClassOnReifiedTypeInLambda-advanced.kt") + public void testKClassOnReifiedTypeInLambda_advanced() throws Exception { + runTest("js/js.translator/testData/box/reflection/kClassOnReifiedTypeInLambda-advanced.kt"); + } + @TestMetadata("kClassSimpleName.kt") public void testKClassSimpleName() throws Exception { runTest("js/js.translator/testData/box/reflection/kClassSimpleName.kt"); @@ -6713,6 +6718,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/reflection/kClassToAndFromJsClass.kt"); } + @TestMetadata("primitiveKClassOnReifiedType.kt") + public void testPrimitiveKClassOnReifiedType() throws Exception { + runTest("js/js.translator/testData/box/reflection/primitiveKClassOnReifiedType.kt"); + } + @TestMetadata("primitives.kt") public void testPrimitives() throws Exception { runTest("js/js.translator/testData/box/reflection/primitives.kt"); 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 3af026604aa..d7a4aafdb43 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 @@ -6738,6 +6738,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/reflection/kClassOnReifiedTypeInLambda.kt"); } + @TestMetadata("kClassOnReifiedTypeInLambda-advanced.kt") + public void testKClassOnReifiedTypeInLambda_advanced() throws Exception { + runTest("js/js.translator/testData/box/reflection/kClassOnReifiedTypeInLambda-advanced.kt"); + } + @TestMetadata("kClassSimpleName.kt") public void testKClassSimpleName() throws Exception { runTest("js/js.translator/testData/box/reflection/kClassSimpleName.kt"); @@ -6748,6 +6753,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/reflection/kClassToAndFromJsClass.kt"); } + @TestMetadata("primitiveKClassOnReifiedType.kt") + public void testPrimitiveKClassOnReifiedType() throws Exception { + runTest("js/js.translator/testData/box/reflection/primitiveKClassOnReifiedType.kt"); + } + @TestMetadata("primitives.kt") public void testPrimitives() throws Exception { runTest("js/js.translator/testData/box/reflection/primitives.kt"); 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 e06ce9a091f..50aa03ec886 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 @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention; import org.jetbrains.kotlin.js.backend.ast.*; import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; +import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction; import org.jetbrains.kotlin.js.naming.NameSuggestion; import org.jetbrains.kotlin.js.translate.context.TranslationContext; import org.jetbrains.kotlin.js.translate.declaration.ClassTranslator; @@ -288,11 +289,11 @@ public final class ExpressionVisitor extends TranslatorVisitor { public static JsExpression getObjectKClass(@NotNull TranslationContext context, @Nullable ClassifierDescriptor descriptor) { JsExpression primitiveExpression = getPrimitiveClass(context, descriptor); if (primitiveExpression != null) return primitiveExpression; - return new JsInvocation(context.getReferenceToIntrinsic(GET_KCLASS), UtilsKt.getReferenceToJsClass(descriptor, context)); + return new JsInvocation(context.getNameForSpecialFunction(SpecialFunction.GET_KCLASS).makeRef(), UtilsKt.getReferenceToJsClass(descriptor, context)); } @Nullable - private static JsExpression getPrimitiveClass(@NotNull TranslationContext context, @Nullable ClassifierDescriptor classifierDescriptor) { + public static JsExpression getPrimitiveClass(@NotNull TranslationContext context, @Nullable ClassifierDescriptor classifierDescriptor) { if (!context.getConfig().isAtLeast(LanguageVersion.KOTLIN_1_2) || findPrimitiveClassesObject(context) == null) return null; if (!(classifierDescriptor instanceof ClassDescriptor)) return null; ClassDescriptor descriptor = (ClassDescriptor) classifierDescriptor; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt index 5a8a7cc340e..dd240727ff9 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.* import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext +import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction @@ -141,6 +142,8 @@ fun getReferenceToJsClass(classifierDescriptor: ClassifierDescriptor?, context: else -> { throw IllegalStateException("Can't get reference for $classifierDescriptor") } + }.also { + it.primitiveKClass = ExpressionVisitor.getPrimitiveClass(context, classifierDescriptor) } } diff --git a/js/js.translator/testData/box/reflection/kClassOnReifiedType.kt b/js/js.translator/testData/box/reflection/kClassOnReifiedType.kt index ef65b2f1464..dc40bfdccf4 100644 --- a/js/js.translator/testData/box/reflection/kClassOnReifiedType.kt +++ b/js/js.translator/testData/box/reflection/kClassOnReifiedType.kt @@ -1,4 +1,8 @@ // EXPECTED_REACHABLE_NODES: 1318 + +// FILE: lib.kt +// RECOMPILE + package foo import kotlin.reflect.KClass @@ -10,11 +14,17 @@ inline fun foo(b: Boolean = false): KClass { return T::class } +// FILE: main.kt +package foo + fun box(): String { check(A::class, foo()) check(B::class, foo()) check(O::class, foo()) check(E::class, foo()) + check(Int::class, foo()) + check(ByteArray::class, foo()) + return "OK" } diff --git a/js/js.translator/testData/box/reflection/kClassOnReifiedTypeInLambda-advanced.kt b/js/js.translator/testData/box/reflection/kClassOnReifiedTypeInLambda-advanced.kt new file mode 100644 index 00000000000..07304a14cc8 --- /dev/null +++ b/js/js.translator/testData/box/reflection/kClassOnReifiedTypeInLambda-advanced.kt @@ -0,0 +1,30 @@ +// EXPECTED_REACHABLE_NODES: 1320 +// IGNORE_BACKEND: JS + +// FILE: lib.kt +// RECOMPILE +package foo + +import kotlin.reflect.KClass + +inline fun foo(b: Boolean = false): () -> KClass { + if (b) { + val T = 1 + } + return { T::class } +} + +// FILE: main.kt +package foo + +fun box(): String { + check(A::class, foo()()) + check(B::class, foo()()) + check(O::class, foo()()) + check(E::class, foo()()) + + check(Int::class, foo()()) + check(ByteArray::class, foo()()) + + return "OK" +} diff --git a/js/js.translator/testData/box/reflection/primitiveKClassOnReifiedType.kt b/js/js.translator/testData/box/reflection/primitiveKClassOnReifiedType.kt new file mode 100644 index 00000000000..11bef0c7112 --- /dev/null +++ b/js/js.translator/testData/box/reflection/primitiveKClassOnReifiedType.kt @@ -0,0 +1,51 @@ +// EXPECTED_REACHABLE_NODES: 1321 + +// MODULE: lib +// FILE: lib.kt +inline fun klassLib() = T::class + +// MODULE: main(lib) +// FILE: main.kt + +import kotlin.reflect.KClass + +inline fun klass() = T::class + +fun box(): String { + check(js("Object"), "Any", klass()) + check(js("String"), "String", klass()) + check(js("Boolean"), "Boolean", klass()) + check(js("Error"), "Throwable", klass()) + check(js("Array"), "Array", klass>()) + check(js("Function"), "Function0", klass>()) + check(js("Function"), "Function1", klass>()) + + check(js("Number"), "Byte", klass()) + check(js("Number"), "Short", klass()) + check(js("Number"), "Int", klass()) + check(js("Number"), "Float", klass()) + check(js("Number"), "Double", klass()) + check(js("Number"), "Number", klass()) + + check(js("Array"), "BooleanArray", klass()) + check(js("Uint16Array"), "CharArray", klass()) + check(js("Int8Array"), "ByteArray", klass()) + check(js("Int16Array"), "ShortArray", klass()) + check(js("Int32Array"), "IntArray", klass()) + check(js("Array"), "LongArray", klass()) + check(js("Float32Array"), "FloatArray", klass()) + check(js("Float64Array"), "DoubleArray", klass()) + + // Check same instance + if (Int::class !== klass()) return "Same instance check failed" + + // Check inlining from other module works + check(js("Int8Array"), "ByteArray", klassLib()) + + return "OK" +} + +private fun check(nativeClass: dynamic, simpleName: String, c: KClass<*>) { + assertEquals(simpleName, c.simpleName, "Simple name of class has unexpected value") + assertEquals(nativeClass, c.js, "Kotlin class does not correspond native class ${nativeClass.name}") +}