diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt index 07c23aed797..1e3275b8ae9 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.kt @@ -69,7 +69,8 @@ abstract class InlineCodegen( private val initialFrameSize = codegen.frameMap.currentSize - private val reifiedTypeInliner = ReifiedTypeInliner(typeParameterMappings, state.languageVersionSettings.isReleaseCoroutines()) + private val reifiedTypeInliner = + ReifiedTypeInliner(typeParameterMappings, state.typeMapper, state.languageVersionSettings.isReleaseCoroutines()) protected val functionDescriptor: FunctionDescriptor = if (InlineUtil.isArrayConstructorWithLambda(function)) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt index 8be634909cf..93095b6484d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/ReifiedTypeInliner.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.codegen.generateAsCast import org.jetbrains.kotlin.codegen.generateIsCheck import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.optimization.common.intConstant +import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.Variance @@ -60,9 +61,13 @@ class ReificationArgument( } } -class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?, private val isReleaseCoroutines: Boolean) { +class ReifiedTypeInliner( + private val parametersMapping: TypeParameterMappings?, + private val typeMapper: KotlinTypeMapper, + private val isReleaseCoroutines: Boolean +) { enum class OperationKind { - NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED; + NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED, TYPE_OF; val id: Int get() = ordinal } @@ -143,6 +148,7 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?, OperationKind.IS -> processIs(insn, instructions, kotlinType, asmType) OperationKind.JAVA_CLASS -> processJavaClass(insn, asmType) OperationKind.ENUM_REIFIED -> processSpecialEnumFunction(insn, instructions, asmType) + OperationKind.TYPE_OF -> processTypeOf(insn, instructions, kotlinType) } ) { instructions.remove(insn.previous.previous!!) // PUSH operation ID @@ -201,6 +207,21 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?, return true } + private fun processTypeOf( + insn: MethodInsnNode, + instructions: InsnList, + kotlinType: KotlinType + ) = rewriteNextTypeInsn(insn, Opcodes.ACONST_NULL) { stubConstNull: AbstractInsnNode -> + val newMethodNode = MethodNode(Opcodes.API_VERSION) + val stackSize = generateTypeOf(InstructionAdapter(newMethodNode), kotlinType, typeMapper) + + instructions.insert(insn, newMethodNode.instructions) + instructions.remove(stubConstNull) + + maxStackSize = Math.max(maxStackSize, stackSize) + return true + } + inline private fun rewriteNextTypeInsn( marker: MethodInsnNode, expectedNextOpcode: Int, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineIntrinsics.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineIntrinsics.kt index c5ffa406eee..f2322f393f0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineIntrinsics.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineIntrinsics.kt @@ -20,9 +20,13 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.resolve.calls.checkers.TypeOfChecker import org.jetbrains.kotlin.resolve.calls.checkers.isBuiltInCoroutineContext import org.jetbrains.kotlin.resolve.jvm.AsmTypes.* import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeProjection +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.typeUtil.builtIns import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -38,6 +42,8 @@ internal fun generateInlineIntrinsic( return when { isSpecialEnumMethod(descriptor) -> createSpecialEnumMethodBody(descriptor.name.asString(), typeArguments!!.keys.single().defaultType, typeMapper) + TypeOfChecker.isTypeOf(descriptor) -> + createTypeOfMethodBody(typeArguments!!.keys.single().defaultType) descriptor.isBuiltInIntercepted(languageVersionSettings) -> createMethodNodeForIntercepted(descriptor, typeMapper, languageVersionSettings) descriptor.isBuiltInCoroutineContext(languageVersionSettings) -> @@ -94,3 +100,101 @@ private fun createSpecialEnumMethodBody(name: String, type: KotlinType, typeMapp internal fun getSpecialEnumFunDescriptor(type: Type, isValueOf: Boolean): String = if (isValueOf) Type.getMethodDescriptor(type, JAVA_STRING_TYPE) else Type.getMethodDescriptor(AsmUtil.getArrayType(type)) + +private fun createTypeOfMethodBody(type: KotlinType): MethodNode { + val node = MethodNode(Opcodes.API_VERSION, Opcodes.ACC_STATIC, "fake", Type.getMethodDescriptor(K_TYPE), null, null) + val v = InstructionAdapter(node) + + putTypeOfReifiedTypeParameter(v, type) + v.areturn(K_TYPE) + + v.visitMaxs(2, 0) + + return node +} + +private fun putTypeOfReifiedTypeParameter(v: InstructionAdapter, type: KotlinType) { + ExpressionCodegen.putReifiedOperationMarkerIfTypeIsReifiedParameterWithoutPropagation(type, ReifiedTypeInliner.OperationKind.TYPE_OF, v) + v.aconst(null) +} + +// Returns some upper bound on maximum stack size +internal fun generateTypeOf(v: InstructionAdapter, kotlinType: KotlinType, typeMapper: KotlinTypeMapper): Int { + val asmType = typeMapper.mapType(kotlinType) + AsmUtil.putJavaLangClassInstance(v, asmType, kotlinType, typeMapper) + + val arguments = kotlinType.arguments + val useArray = arguments.size >= 3 + + if (useArray) { + v.iconst(arguments.size) + v.newarray(K_TYPE_PROJECTION) + } + + var maxStackSize = 3 + + for (i in 0 until arguments.size) { + if (useArray) { + v.dup() + v.iconst(i) + } + + val stackSize = doGenerateTypeProjection(v, arguments[i], typeMapper) + maxStackSize = maxOf(maxStackSize, stackSize + i + 5) + + if (useArray) { + v.astore(K_TYPE_PROJECTION) + } + } + + val methodName = if (kotlinType.isMarkedNullable) "nullableTypeOf" else "typeOf" + + val projections = when (arguments.size) { + 0 -> emptyArray() + 1 -> arrayOf(K_TYPE_PROJECTION) + 2 -> arrayOf(K_TYPE_PROJECTION, K_TYPE_PROJECTION) + else -> arrayOf(AsmUtil.getArrayType(K_TYPE_PROJECTION)) + } + val signature = Type.getMethodDescriptor(K_TYPE, JAVA_CLASS_TYPE, *projections) + + v.invokestatic(REFLECTION, methodName, signature, false) + + return maxStackSize +} + +private fun doGenerateTypeProjection( + v: InstructionAdapter, + projection: TypeProjection, + typeMapper: KotlinTypeMapper +): Int { + // KTypeProjection members could be static, see KT-30083 and KT-30084 + v.getstatic(K_TYPE_PROJECTION.internalName, "Companion", K_TYPE_PROJECTION_COMPANION.descriptor) + + if (projection.isStarProjection) { + v.invokevirtual(K_TYPE_PROJECTION_COMPANION.internalName, "getSTAR", Type.getMethodDescriptor(K_TYPE_PROJECTION), false) + return 1 + } + + val type = projection.type + val descriptor = type.constructor.declarationDescriptor + val stackSize = if (descriptor is TypeParameterDescriptor) { + if (descriptor.isReified) { + putTypeOfReifiedTypeParameter(v, type) + 2 + } else { + // TODO: support non-reified type parameters in typeOf + generateTypeOf(v, type.builtIns.nullableAnyType, typeMapper) + } + } else { + generateTypeOf(v, type, typeMapper) + } + + val methodName = when (projection.projectionKind) { + Variance.INVARIANT -> "invariant" + Variance.IN_VARIANCE -> "contravariant" + Variance.OUT_VARIANCE -> "covariant" + } + v.invokevirtual(K_TYPE_PROJECTION_COMPANION.internalName, methodName, Type.getMethodDescriptor(K_TYPE_PROJECTION, K_TYPE), false) + + return stackSize + 1 +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java index 18c72ba64e7..4bf5a8f82f0 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/AsmTypes.java @@ -63,6 +63,10 @@ public class AsmTypes { public static final Type K_MUTABLE_PROPERTY1_TYPE = reflect("KMutableProperty1"); public static final Type K_MUTABLE_PROPERTY2_TYPE = reflect("KMutableProperty2"); + public static final Type K_TYPE = reflect("KType"); + public static final Type K_TYPE_PROJECTION = reflect("KTypeProjection"); + public static final Type K_TYPE_PROJECTION_COMPANION = reflect("KTypeProjection$Companion"); + public static final Type SUSPEND_FUNCTION_TYPE = Type.getObjectType("kotlin/coroutines/jvm/internal/SuspendFunction"); public static final String REFLECTION = "kotlin/jvm/internal/Reflection"; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt index 52a0e6e9734..eb63507e10c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/PlatformConfiguratorBase.kt @@ -45,7 +45,7 @@ private val DEFAULT_CALL_CHECKERS = listOf( UnderscoreUsageChecker, AssigningNamedArgumentToVarargChecker(), PrimitiveNumericComparisonCallChecker, LambdaWithSuspendModifierCallChecker, UselessElvisCallChecker(), ResultTypeWithNullableOperatorsChecker(), NullableVarargArgumentCallChecker, - NamedFunAsExpressionChecker, ContractNotAllowedCallChecker, ReifiedTypeParameterSubstitutionChecker() + NamedFunAsExpressionChecker, ContractNotAllowedCallChecker, ReifiedTypeParameterSubstitutionChecker(), TypeOfChecker ) private val DEFAULT_TYPE_CHECKERS = emptyList() private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/TypeOfChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/TypeOfChecker.kt new file mode 100644 index 00000000000..2e3169e0b00 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/TypeOfChecker.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.resolve.calls.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.builtins.KOTLIN_REFLECT_FQ_NAME +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.types.typeUtil.contains + +object TypeOfChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + if (!isTypeOf(resolvedCall.resultingDescriptor)) return + + for ((_, argument) in resolvedCall.typeArguments) { + if (argument.contains { type -> + val descriptor = type.constructor.declarationDescriptor + descriptor is TypeParameterDescriptor && !descriptor.isReified + }) { + context.trace.report(Errors.UNSUPPORTED.on(reportOn, "'typeOf' with non-reified type parameters is not supported")) + } + } + } + + fun isTypeOf(descriptor: CallableDescriptor): Boolean = + descriptor.name.asString() == "typeOf" && + descriptor.valueParameters.isEmpty() && + (descriptor.containingDeclaration as? PackageFragmentDescriptor)?.fqName == KOTLIN_REFLECT_FQ_NAME +} diff --git a/compiler/testData/codegen/box/reflection/typeOf/classes.kt b/compiler/testData/codegen/box/reflection/typeOf/classes.kt new file mode 100644 index 00000000000..7c6d05ea9a6 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/classes.kt @@ -0,0 +1,41 @@ +// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi +// IGNORE_BACKEND: JS, JS_IR, NATIVE +// WITH_REFLECT + +package test + +import kotlin.reflect.KType +import kotlin.reflect.typeOf +import kotlin.test.assertEquals + +class C + +fun check(expected: String, actual: KType) { + assertEquals(expected, actual.toString()) +} + +fun box(): String { + check("kotlin.Any", typeOf()) + check("kotlin.String", typeOf()) + check("kotlin.String?", typeOf()) + check("kotlin.Unit", typeOf()) + + check("test.C", typeOf()) + check("test.C?", typeOf()) + + check("kotlin.collections.List", typeOf>()) + check("kotlin.collections.Map?", typeOf?>()) + check("kotlin.Enum>", typeOf>()) + check("kotlin.Enum", typeOf>()) + + check("kotlin.Array", typeOf>()) + check("kotlin.Array", typeOf>()) + check("kotlin.Array", typeOf>()) + check("kotlin.Array?>", typeOf?>>()) + + check("kotlin.Int", typeOf()) + check("kotlin.Int?", typeOf()) + check("kotlin.Boolean", typeOf()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/typeOf/inlineClasses.kt b/compiler/testData/codegen/box/reflection/typeOf/inlineClasses.kt new file mode 100644 index 00000000000..97241cd2d14 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/inlineClasses.kt @@ -0,0 +1,32 @@ +// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi +// IGNORE_BACKEND: JVM_IR, JS, JS_IR, NATIVE +// WITH_REFLECT + +package test + +import kotlin.reflect.KType +import kotlin.reflect.typeOf +import kotlin.test.assertEquals + +inline class Z(val value: String) + +fun check(expected: String, actual: KType) { + assertEquals(expected, actual.toString()) +} + +fun box(): String { + check("test.Z", typeOf()) + check("test.Z?", typeOf()) + check("kotlin.Array", typeOf>()) + check("kotlin.Array", typeOf>()) + + check("kotlin.UInt", typeOf()) + check("kotlin.UInt?", typeOf()) + check("kotlin.ULong?", typeOf()) + check("kotlin.UShortArray", typeOf()) + check("kotlin.UShortArray?", typeOf()) + check("kotlin.Array", typeOf>()) + check("kotlin.Array?", typeOf?>()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt b/compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt new file mode 100644 index 00000000000..06c8f6139d2 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt @@ -0,0 +1,31 @@ +// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi +// IGNORE_BACKEND: JS, JS_IR, NATIVE +// WITH_REFLECT + +package test + +import kotlin.reflect.typeOf +import kotlin.test.assertEquals + +interface I1 +interface I2 +interface I3 +interface I4 +interface I5 +interface I6 +interface I7 + +class C + +fun box(): String { + assertEquals( + "test.C", + typeOf>().toString() + ) + assertEquals( + "test.C?", + typeOf?>().toString() + ) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt b/compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt new file mode 100644 index 00000000000..810c16f45bd --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt @@ -0,0 +1,22 @@ +// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi +// IGNORE_BACKEND: JS, JS_IR, NATIVE +// WITH_REFLECT + +package test + +import kotlin.reflect.typeOf +import kotlin.test.assertEquals + +interface C + +inline fun get() = typeOf() + +inline fun get1() = get() + +inline fun get2() = get1>>() + +fun box(): String { + assertEquals("kotlin.collections.Map>?", get2().toString()) + assertEquals("kotlin.collections.Map?, kotlin.Array>>?", get2>().toString()) + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/typeOf/noReflect/classes.kt b/compiler/testData/codegen/box/reflection/typeOf/noReflect/classes.kt new file mode 100644 index 00000000000..622c654204e --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/noReflect/classes.kt @@ -0,0 +1,41 @@ +// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi +// TARGET_BACKEND: JVM +// WITH_RUNTIME + +package test + +import kotlin.reflect.KType +import kotlin.reflect.typeOf +import kotlin.test.assertEquals + +class C + +fun check(expected: String, actual: KType) { + assertEquals(expected + " (Kotlin reflection is not available)", actual.toString()) +} + +fun box(): String { + check("java.lang.Object", typeOf()) + check("java.lang.String", typeOf()) + check("java.lang.String?", typeOf()) + check("kotlin.Unit", typeOf()) + + check("test.C", typeOf()) + check("test.C?", typeOf()) + + check("java.util.List", typeOf>()) + check("java.util.Map?", typeOf?>()) + check("java.lang.Enum>", typeOf>()) + check("java.lang.Enum", typeOf>()) + + check("kotlin.Array", typeOf>()) + check("kotlin.Array", typeOf>()) + check("kotlin.Array", typeOf>()) + check("kotlin.Array?>", typeOf?>>()) + + check("int", typeOf()) + check("java.lang.Integer?", typeOf()) + check("boolean", typeOf()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/typeOf/noReflect/inlineClasses.kt b/compiler/testData/codegen/box/reflection/typeOf/noReflect/inlineClasses.kt new file mode 100644 index 00000000000..a3e8bd2667d --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/noReflect/inlineClasses.kt @@ -0,0 +1,33 @@ +// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi +// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JVM_IR +// WITH_RUNTIME + +package test + +import kotlin.reflect.KType +import kotlin.reflect.typeOf +import kotlin.test.assertEquals + +inline class Z(val value: String) + +fun check(expected: String, actual: KType) { + assertEquals(expected + " (Kotlin reflection is not available)", actual.toString()) +} + +fun box(): String { + check("test.Z", typeOf()) + check("test.Z?", typeOf()) + check("kotlin.Array", typeOf>()) + check("kotlin.Array", typeOf>()) + + check("kotlin.UInt", typeOf()) + check("kotlin.UInt?", typeOf()) + check("kotlin.ULong?", typeOf()) + check("kotlin.UShortArray", typeOf()) + check("kotlin.UShortArray?", typeOf()) + check("kotlin.Array", typeOf>()) + check("kotlin.Array?", typeOf?>()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/typeOf/noReflect/typeReferenceEqualsHashCode.kt b/compiler/testData/codegen/box/reflection/typeOf/noReflect/typeReferenceEqualsHashCode.kt new file mode 100644 index 00000000000..768bb5cab9f --- /dev/null +++ b/compiler/testData/codegen/box/reflection/typeOf/noReflect/typeReferenceEqualsHashCode.kt @@ -0,0 +1,54 @@ +// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi +// TARGET_BACKEND: JVM +// WITH_RUNTIME + +package test + +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +class C + +fun assertEqual(a: KType, b: KType) { + if (a != b || b != a) throw AssertionError("Fail equals: $a != $b") + if (a.hashCode() != b.hashCode()) throw AssertionError("Fail hashCode: $a != $b") +} + +fun assertNotEqual(a: KType, b: KType) { + if (a == b || b == a) throw AssertionError("Fail equals: $a == $b") +} + +inline fun equal() { + assertEqual(typeOf(), typeOf()) +} + +inline fun notEqual() { + assertNotEqual(typeOf(), typeOf()) +} + +fun box(): String { + equal() + equal() + equal() + + equal() + equal() + + equal, List>() + equal, Enum>() + + equal, Array>() + equal, Array>() + equal, Array>() // This is subject to change if we retain star projections in typeOf + + equal() + equal() + + notEqual() + notEqual() + notEqual, List>() + notEqual, Map>() + notEqual, Array>>() + + return "OK" +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/reflection/typeOfWithNonReifiedParameter.kt b/compiler/testData/diagnostics/testsWithStdLib/reflection/typeOfWithNonReifiedParameter.kt new file mode 100644 index 00000000000..3149fbf0f93 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/reflection/typeOfWithNonReifiedParameter.kt @@ -0,0 +1,34 @@ +// !USE_EXPERIMENTAL: kotlin.ExperimentalStdlibApi + +import kotlin.reflect.typeOf + +inline fun test1() { + typeOf<X>() + typeOf>() + typeOf>() + + typeOf() + + typeOf<Z>() + typeOf?>() + typeOf>() +} + + +class Test2 { + fun test2() { + typeOf<W>() + typeOf>() + typeOf>() + } +} + + +inline fun f() { + typeOf() +} + +fun test3() { + // We don't report anything here because we can't know in frontend how the corresponding type parameter is used in f + f>() +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/reflection/typeOfWithNonReifiedParameter.txt b/compiler/testData/diagnostics/testsWithStdLib/reflection/typeOfWithNonReifiedParameter.txt new file mode 100644 index 00000000000..1fd20e67728 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/reflection/typeOfWithNonReifiedParameter.txt @@ -0,0 +1,13 @@ +package + +public inline fun f(): kotlin.Unit +public inline fun test1(): kotlin.Unit +public fun test3(): kotlin.Unit + +public final class Test2 { + public constructor Test2() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final fun test2(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index e331461cdff..f6d679f18bd 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -2941,6 +2941,24 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/reflection") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Reflection extends AbstractDiagnosticsTestWithStdLib { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInReflection() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/reflection"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("typeOfWithNonReifiedParameter.kt") + public void testTypeOfWithNonReifiedParameter() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/reflection/typeOfWithNonReifiedParameter.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/regression") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index 363b379e45b..3fac669c5f3 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -2941,6 +2941,24 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/reflection") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Reflection extends AbstractDiagnosticsTestWithStdLibUsingJavac { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInReflection() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/reflection"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("typeOfWithNonReifiedParameter.kt") + public void testTypeOfWithNonReifiedParameter() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/reflection/typeOfWithNonReifiedParameter.kt"); + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/regression") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 84797c2ba06..fa645a33d90 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -21487,6 +21487,67 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/reflection/typeOf") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TypeOf extends AbstractBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInTypeOf() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("classes.kt") + public void testClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/classes.kt"); + } + + @TestMetadata("inlineClasses.kt") + public void testInlineClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/inlineClasses.kt"); + } + + @TestMetadata("manyTypeArguments.kt") + public void testManyTypeArguments() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt"); + } + + @TestMetadata("multipleLayers.kt") + public void testMultipleLayers() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt"); + } + + @TestMetadata("compiler/testData/codegen/box/reflection/typeOf/noReflect") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NoReflect extends AbstractBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInNoReflect() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf/noReflect"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("classes.kt") + public void testClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/classes.kt"); + } + + @TestMetadata("inlineClasses.kt") + public void testInlineClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/inlineClasses.kt"); + } + + @TestMetadata("typeReferenceEqualsHashCode.kt") + public void testTypeReferenceEqualsHashCode() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/typeReferenceEqualsHashCode.kt"); + } + } + } + @TestMetadata("compiler/testData/codegen/box/reflection/typeParameters") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 7cafa7d3cb5..0f76d16ba53 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -21487,6 +21487,67 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes } } + @TestMetadata("compiler/testData/codegen/box/reflection/typeOf") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TypeOf extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInTypeOf() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("classes.kt") + public void testClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/classes.kt"); + } + + @TestMetadata("inlineClasses.kt") + public void testInlineClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/inlineClasses.kt"); + } + + @TestMetadata("manyTypeArguments.kt") + public void testManyTypeArguments() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt"); + } + + @TestMetadata("multipleLayers.kt") + public void testMultipleLayers() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt"); + } + + @TestMetadata("compiler/testData/codegen/box/reflection/typeOf/noReflect") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NoReflect extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInNoReflect() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf/noReflect"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("classes.kt") + public void testClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/classes.kt"); + } + + @TestMetadata("inlineClasses.kt") + public void testInlineClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/inlineClasses.kt"); + } + + @TestMetadata("typeReferenceEqualsHashCode.kt") + public void testTypeReferenceEqualsHashCode() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/typeReferenceEqualsHashCode.kt"); + } + } + } + @TestMetadata("compiler/testData/codegen/box/reflection/typeParameters") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index b9a04ad04f1..8fffcfb1e1a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -21492,6 +21492,67 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/reflection/typeOf") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TypeOf extends AbstractIrBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInTypeOf() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); + } + + @TestMetadata("classes.kt") + public void testClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/classes.kt"); + } + + @TestMetadata("inlineClasses.kt") + public void testInlineClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/inlineClasses.kt"); + } + + @TestMetadata("manyTypeArguments.kt") + public void testManyTypeArguments() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt"); + } + + @TestMetadata("multipleLayers.kt") + public void testMultipleLayers() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt"); + } + + @TestMetadata("compiler/testData/codegen/box/reflection/typeOf/noReflect") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NoReflect extends AbstractIrBlackBoxCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInNoReflect() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf/noReflect"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); + } + + @TestMetadata("classes.kt") + public void testClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/classes.kt"); + } + + @TestMetadata("inlineClasses.kt") + public void testInlineClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/inlineClasses.kt"); + } + + @TestMetadata("typeReferenceEqualsHashCode.kt") + public void testTypeReferenceEqualsHashCode() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/noReflect/typeReferenceEqualsHashCode.kt"); + } + } + } + @TestMetadata("compiler/testData/codegen/box/reflection/typeParameters") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java index c5a45b6e5d4..97b01a90506 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java @@ -18,8 +18,13 @@ package kotlin.reflect.jvm.internal; import kotlin.jvm.internal.*; import kotlin.reflect.*; +import kotlin.reflect.full.KClassifiers; import kotlin.reflect.jvm.ReflectLambdaKt; +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.List; + /** * @suppress */ @@ -111,6 +116,13 @@ public class ReflectionFactoryImpl extends ReflectionFactory { return owner instanceof KDeclarationContainerImpl ? ((KDeclarationContainerImpl) owner) : EmptyContainerForLocal.INSTANCE; } + // typeOf + + @Override + public KType typeOf(KClassifier klass, List arguments, boolean isMarkedNullable) { + return KClassifiers.createType(klass, arguments, isMarkedNullable, Collections.emptyList()); + } + // Misc public static void clearCaches() { diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index b694819c35c..a9e291e83ca 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -16587,6 +16587,52 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/reflection/typeOf") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TypeOf extends AbstractIrJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + public void testAllFilesPresentInTypeOf() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS_IR, true); + } + + @TestMetadata("classes.kt") + public void testClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/classes.kt"); + } + + @TestMetadata("inlineClasses.kt") + public void testInlineClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/inlineClasses.kt"); + } + + @TestMetadata("manyTypeArguments.kt") + public void testManyTypeArguments() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt"); + } + + @TestMetadata("multipleLayers.kt") + public void testMultipleLayers() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt"); + } + + @TestMetadata("compiler/testData/codegen/box/reflection/typeOf/noReflect") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NoReflect extends AbstractIrJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + public void testAllFilesPresentInNoReflect() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf/noReflect"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS_IR, true); + } + } + } + @TestMetadata("compiler/testData/codegen/box/reflection/typeParameters") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 991152ff63c..0ae6d333b4a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -17682,6 +17682,52 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/reflection/typeOf") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TypeOf extends AbstractJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); + } + + public void testAllFilesPresentInTypeOf() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("classes.kt") + public void testClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/classes.kt"); + } + + @TestMetadata("inlineClasses.kt") + public void testInlineClasses() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/inlineClasses.kt"); + } + + @TestMetadata("manyTypeArguments.kt") + public void testManyTypeArguments() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/manyTypeArguments.kt"); + } + + @TestMetadata("multipleLayers.kt") + public void testMultipleLayers() throws Exception { + runTest("compiler/testData/codegen/box/reflection/typeOf/multipleLayers.kt"); + } + + @TestMetadata("compiler/testData/codegen/box/reflection/typeOf/noReflect") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NoReflect extends AbstractJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); + } + + public void testAllFilesPresentInNoReflect() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/typeOf/noReflect"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + } + } + @TestMetadata("compiler/testData/codegen/box/reflection/typeParameters") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/Reflection.java b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/Reflection.java index 24e7f6fb206..ee3334d88af 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/Reflection.java +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/Reflection.java @@ -6,8 +6,12 @@ package kotlin.jvm.internal; import kotlin.SinceKotlin; +import kotlin.collections.ArraysKt; import kotlin.reflect.*; +import java.util.Arrays; +import java.util.Collections; + /** * This class serves as a facade to the actual reflection implementation. JVM back-end generates calls to static methods of this class * on any reflection-using construct. @@ -105,4 +109,46 @@ public class Reflection { public static KMutableProperty2 mutableProperty2(MutablePropertyReference2 p) { return factory.mutableProperty2(p); } + + // typeOf + + @SinceKotlin(version = "1.4") + public static KType typeOf(Class klass) { + return factory.typeOf(getOrCreateKotlinClass(klass), Collections.emptyList(), false); + } + + @SinceKotlin(version = "1.4") + public static KType typeOf(Class klass, KTypeProjection arg1) { + return factory.typeOf(getOrCreateKotlinClass(klass), Collections.singletonList(arg1), false); + } + + @SinceKotlin(version = "1.4") + public static KType typeOf(Class klass, KTypeProjection arg1, KTypeProjection arg2) { + return factory.typeOf(getOrCreateKotlinClass(klass), Arrays.asList(arg1, arg2), false); + } + + @SinceKotlin(version = "1.4") + public static KType typeOf(Class klass, KTypeProjection... arguments) { + return factory.typeOf(getOrCreateKotlinClass(klass), ArraysKt.toList(arguments), false); + } + + @SinceKotlin(version = "1.4") + public static KType nullableTypeOf(Class klass) { + return factory.typeOf(getOrCreateKotlinClass(klass), Collections.emptyList(), true); + } + + @SinceKotlin(version = "1.4") + public static KType nullableTypeOf(Class klass, KTypeProjection arg1) { + return factory.typeOf(getOrCreateKotlinClass(klass), Collections.singletonList(arg1), true); + } + + @SinceKotlin(version = "1.4") + public static KType nullableTypeOf(Class klass, KTypeProjection arg1, KTypeProjection arg2) { + return factory.typeOf(getOrCreateKotlinClass(klass), Arrays.asList(arg1, arg2), true); + } + + @SinceKotlin(version = "1.4") + public static KType nullableTypeOf(Class klass, KTypeProjection... arguments) { + return factory.typeOf(getOrCreateKotlinClass(klass), ArraysKt.toList(arguments), true); + } } diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ReflectionFactory.java b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ReflectionFactory.java index f7f02e9b4b3..a25b8d004be 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ReflectionFactory.java +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ReflectionFactory.java @@ -8,6 +8,8 @@ package kotlin.jvm.internal; import kotlin.SinceKotlin; import kotlin.reflect.*; +import java.util.List; + public class ReflectionFactory { private static final String KOTLIN_JVM_FUNCTIONS = "kotlin.jvm.functions."; @@ -73,4 +75,11 @@ public class ReflectionFactory { public KMutableProperty2 mutableProperty2(MutablePropertyReference2 p) { return p; } + + // typeOf + + @SinceKotlin(version = "1.4") + public KType typeOf(KClassifier klass, List arguments, boolean isMarkedNullable) { + return new TypeReference(klass, arguments, isMarkedNullable); + } } diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/TypeReference.kt b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/TypeReference.kt new file mode 100644 index 00000000000..afec0ecd4e7 --- /dev/null +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/TypeReference.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin.jvm.internal + +import kotlin.reflect.* + +@SinceKotlin("1.4") +@Suppress("NEWER_VERSION_IN_SINCE_KOTLIN", "API_NOT_AVAILABLE" /* See KT-30129 */) // TODO: remove this in 1.4 +public class TypeReference( + override val classifier: KClassifier, + override val arguments: List, + override val isMarkedNullable: Boolean +) : KType { + override val annotations: List + get() = emptyList() + + override fun equals(other: Any?): Boolean = + other is TypeReference && + classifier == other.classifier && arguments == other.arguments && isMarkedNullable == other.isMarkedNullable + + override fun hashCode(): Int = + (classifier.hashCode() * 31 + arguments.hashCode()) * 31 + isMarkedNullable.hashCode() + + override fun toString(): String = + asString() + Reflection.REFLECTION_NOT_AVAILABLE + + private fun asString(): String { + val javaClass = (classifier as? KClass<*>)?.java + val klass = when { + javaClass == null -> classifier.toString() + javaClass.isArray -> javaClass.arrayClassName + else -> javaClass.name + } + val args = + if (arguments.isEmpty()) "" + else arguments.joinToString(", ", "<", ">") { it.asString() } + val nullable = if (isMarkedNullable) "?" else "" + + return klass + args + nullable + } + + private val Class<*>.arrayClassName + get() = when (this) { + BooleanArray::class.java -> "kotlin.BooleanArray" + CharArray::class.java -> "kotlin.CharArray" + ByteArray::class.java -> "kotlin.ByteArray" + ShortArray::class.java -> "kotlin.ShortArray" + IntArray::class.java -> "kotlin.IntArray" + FloatArray::class.java -> "kotlin.FloatArray" + LongArray::class.java -> "kotlin.LongArray" + DoubleArray::class.java -> "kotlin.DoubleArray" + else -> "kotlin.Array" + } + + // TODO: this should be the implementation of KTypeProjection.toString, see KT-30071 + @Suppress("NO_REFLECTION_IN_CLASS_PATH") + private fun KTypeProjection.asString(): String { + if (variance == null) return "*" + + val typeString = (type as? TypeReference)?.asString() ?: type.toString() + return when (variance) { + KVariance.INVARIANT -> typeString + KVariance.IN -> "in $typeString" + KVariance.OUT -> "out $typeString" + } + } +} diff --git a/libraries/stdlib/src/kotlin/reflect/typeOf.kt b/libraries/stdlib/src/kotlin/reflect/typeOf.kt new file mode 100644 index 00000000000..03994820217 --- /dev/null +++ b/libraries/stdlib/src/kotlin/reflect/typeOf.kt @@ -0,0 +1,12 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin.reflect + +@Suppress("unused") // KT-12448 +@SinceKotlin("1.3") +@ExperimentalStdlibApi +public inline fun typeOf(): KType = + throw UnsupportedOperationException("This function is implemented as an intrinsic on all supported platforms.") diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 99b5083a71c..d6848a28d78 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -3531,11 +3531,19 @@ public class kotlin/jvm/internal/Reflection { public static fun mutableProperty0 (Lkotlin/jvm/internal/MutablePropertyReference0;)Lkotlin/reflect/KMutableProperty0; public static fun mutableProperty1 (Lkotlin/jvm/internal/MutablePropertyReference1;)Lkotlin/reflect/KMutableProperty1; public static fun mutableProperty2 (Lkotlin/jvm/internal/MutablePropertyReference2;)Lkotlin/reflect/KMutableProperty2; + public static fun nullableTypeOf (Ljava/lang/Class;)Lkotlin/reflect/KType; + public static fun nullableTypeOf (Ljava/lang/Class;Lkotlin/reflect/KTypeProjection;)Lkotlin/reflect/KType; + public static fun nullableTypeOf (Ljava/lang/Class;Lkotlin/reflect/KTypeProjection;Lkotlin/reflect/KTypeProjection;)Lkotlin/reflect/KType; + public static fun nullableTypeOf (Ljava/lang/Class;[Lkotlin/reflect/KTypeProjection;)Lkotlin/reflect/KType; public static fun property0 (Lkotlin/jvm/internal/PropertyReference0;)Lkotlin/reflect/KProperty0; public static fun property1 (Lkotlin/jvm/internal/PropertyReference1;)Lkotlin/reflect/KProperty1; public static fun property2 (Lkotlin/jvm/internal/PropertyReference2;)Lkotlin/reflect/KProperty2; public static fun renderLambdaToString (Lkotlin/jvm/internal/FunctionBase;)Ljava/lang/String; public static fun renderLambdaToString (Lkotlin/jvm/internal/Lambda;)Ljava/lang/String; + public static fun typeOf (Ljava/lang/Class;)Lkotlin/reflect/KType; + public static fun typeOf (Ljava/lang/Class;Lkotlin/reflect/KTypeProjection;)Lkotlin/reflect/KType; + public static fun typeOf (Ljava/lang/Class;Lkotlin/reflect/KTypeProjection;Lkotlin/reflect/KTypeProjection;)Lkotlin/reflect/KType; + public static fun typeOf (Ljava/lang/Class;[Lkotlin/reflect/KTypeProjection;)Lkotlin/reflect/KType; } public class kotlin/jvm/internal/ReflectionFactory { @@ -3554,6 +3562,7 @@ public class kotlin/jvm/internal/ReflectionFactory { public fun property2 (Lkotlin/jvm/internal/PropertyReference2;)Lkotlin/reflect/KProperty2; public fun renderLambdaToString (Lkotlin/jvm/internal/FunctionBase;)Ljava/lang/String; public fun renderLambdaToString (Lkotlin/jvm/internal/Lambda;)Ljava/lang/String; + public fun typeOf (Lkotlin/reflect/KClassifier;Ljava/util/List;Z)Lkotlin/reflect/KType; } public final class kotlin/jvm/internal/ShortCompanionObject { @@ -3626,6 +3635,17 @@ public class kotlin/jvm/internal/TypeIntrinsics { public static fun throwCce (Ljava/lang/String;)V } +public final class kotlin/jvm/internal/TypeReference : kotlin/reflect/KType { + public fun (Lkotlin/reflect/KClassifier;Ljava/util/List;Z)V + public fun equals (Ljava/lang/Object;)Z + public fun getAnnotations ()Ljava/util/List; + public fun getArguments ()Ljava/util/List; + public fun getClassifier ()Lkotlin/reflect/KClassifier; + public fun hashCode ()I + public fun isMarkedNullable ()Z + public fun toString ()Ljava/lang/String; +} + public abstract interface class kotlin/jvm/internal/markers/KMappedMarker { }