diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index ad4eafd0a24..11a61eb0a0b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -59,7 +59,6 @@ import java.util.Collections; import java.util.List; import static org.jetbrains.kotlin.codegen.AsmUtil.*; -import static org.jetbrains.kotlin.codegen.ExpressionCodegen.generateClassLiteralReference; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConst; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass; @@ -390,8 +389,8 @@ public class ClosureCodegen extends MemberCodegen { DeclarationDescriptor container = descriptor.getContainingDeclaration(); if (container instanceof ClassDescriptor) { // TODO: getDefaultType() here is wrong and won't work for arrays - StackValue value = generateClassLiteralReference(state.getTypeMapper(), ((ClassDescriptor) container).getDefaultType()); - value.put(K_CLASS_TYPE, iv); + putJavaLangClassInstance(iv, state.getTypeMapper().mapType(((ClassDescriptor) container).getDefaultType())); + wrapJavaClassIntoKClass(iv); } else if (container instanceof PackageFragmentDescriptor) { iv.aconst(state.getTypeMapper().mapOwner(descriptor)); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index bd815051aa4..62fd52e6ce4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -92,6 +92,7 @@ import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.TypeProjection; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; +import org.jetbrains.kotlin.types.expressions.DoubleColonLHS; import org.jetbrains.kotlin.types.typesApproximation.CapturedTypeApproximationKt; import org.jetbrains.org.objectweb.asm.Label; import org.jetbrains.org.objectweb.asm.MethodVisitor; @@ -3209,13 +3210,11 @@ public class ExpressionCodegen extends KtVisitor impleme @Override public StackValue visitClassLiteralExpression(@NotNull KtClassLiteralExpression expression, StackValue data) { - KotlinType type = bindingContext.getType(expression); - assert type != null; - - assert state.getReflectionTypes().getKClass().getTypeConstructor().equals(type.getConstructor()) - : "::class expression should be type checked to a KClass: " + type; - - return generateClassLiteralReference(typeMapper, CollectionsKt.single(type.getArguments()).getType(), this); + KtExpression receiverExpression = expression.getReceiverExpression(); + assert receiverExpression != null : "Class literal expression should have a left-hand side"; + DoubleColonLHS lhs = bindingContext.get(DOUBLE_COLON_LHS, receiverExpression); + assert lhs != null : "Class literal expression should have LHS resolved"; + return generateClassLiteralReference(lhs, receiverExpression, /* wrapIntoKClass = */ true); } @Override @@ -3282,28 +3281,31 @@ public class ExpressionCodegen extends KtVisitor impleme } @NotNull - public static StackValue generateClassLiteralReference(@NotNull KotlinTypeMapper typeMapper, @NotNull KotlinType type) { - return generateClassLiteralReference(typeMapper, type, null); - } - - @NotNull - private static StackValue generateClassLiteralReference(@NotNull final KotlinTypeMapper typeMapper, @NotNull final KotlinType type, @Nullable final ExpressionCodegen codegen) { - return StackValue.operation(K_CLASS_TYPE, new Function1() { + public StackValue generateClassLiteralReference( + @NotNull final DoubleColonLHS lhs, + @Nullable final KtExpression receiverExpression, + final boolean wrapIntoKClass + ) { + return StackValue.operation(wrapIntoKClass ? K_CLASS_TYPE : JAVA_CLASS_TYPE, new Function1() { @Override public Unit invoke(InstructionAdapter v) { - Type classAsmType = typeMapper.mapType(type); - ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); - if (descriptor instanceof TypeParameterDescriptor) { - TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) descriptor; - assert typeParameterDescriptor.isReified() : - "Non-reified type parameter under ::class should be rejected by type checker: " + typeParameterDescriptor; - assert codegen != null : - "Reference to member of reified type should be rejected by type checker " + typeParameterDescriptor; - codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.JAVA_CLASS); + KotlinType type = lhs.getType(); + if (lhs instanceof DoubleColonLHS.Expression) { + JavaClassProperty.INSTANCE.generateImpl(v, gen(receiverExpression)); + } + else if (lhs instanceof DoubleColonLHS.Type) { + if (TypeUtils.isTypeParameter(type)) { + assert TypeUtils.isReifiedTypeParameter(type) : + "Non-reified type parameter under ::class should be rejected by type checker: " + type; + putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.JAVA_CLASS); + } + + putJavaLangClassInstance(v, typeMapper.mapType(type)); } - putJavaLangClassInstance(v, classAsmType); - wrapJavaClassIntoKClass(v); + if (wrapIntoKClass) { + wrapJavaClassIntoKClass(v); + } return Unit.INSTANCE; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KClassJavaProperty.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KClassJavaProperty.kt index 04fc9b95964..6be791e5b78 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KClassJavaProperty.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/KClassJavaProperty.kt @@ -16,69 +16,21 @@ package org.jetbrains.kotlin.codegen.intrinsics -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.StackValue -import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.psi.KtClassLiteralExpression +import org.jetbrains.kotlin.resolve.BindingContext.DOUBLE_COLON_LHS import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.org.objectweb.asm.Type -import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter class KClassJavaProperty : IntrinsicPropertyGetter() { override fun generate(resolvedCall: ResolvedCall<*>?, codegen: ExpressionCodegen, returnType: Type, receiver: StackValue): StackValue? { - val receiverType = resolvedCall!!.extensionReceiver!!.type - val type = getKClassTypeArgument(receiverType) ?: return null - val asmType = codegen.state.typeMapper.mapType(type) - - return when { - isReifiedTypeParameter(type) -> { - StackValue.operation(returnType) { iv -> - codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.JAVA_CLASS) - AsmUtil.putJavaLangClassInstance(iv, asmType) - coerceToJavaLangClass(iv, returnType) - } - } - isWithClassLiteralArgument(resolvedCall) -> { - StackValue.operation(returnType) { iv -> - if (AsmUtil.isPrimitive(asmType)) { - iv.getstatic(AsmUtil.boxType(asmType).internalName, "TYPE", "Ljava/lang/Class;") - } - else { - iv.tconst(asmType) - } - coerceToJavaLangClass(iv, returnType) - } - } - else -> null - } - } - - private fun getKClassTypeArgument(type: KotlinType): KotlinType? { - val typeClassifier = type.constructor.declarationDescriptor - return if (typeClassifier is ClassDescriptor && KotlinBuiltIns.isKClass(typeClassifier)) - type.arguments.singleOrNull()?.type - else - null - } - - private fun isReifiedTypeParameter(type: KotlinType): Boolean { - val typeDescriptor = type.constructor.declarationDescriptor - return typeDescriptor is TypeParameterDescriptor && typeDescriptor.isReified - } - - private fun isWithClassLiteralArgument(resolvedCall: ResolvedCall<*>): Boolean { - val extensionReceiver = resolvedCall.extensionReceiver - return extensionReceiver is ExpressionReceiver && extensionReceiver.expression is KtClassLiteralExpression - } - - private fun coerceToJavaLangClass(iv: InstructionAdapter, returnType: Type) { - StackValue.coerce(AsmTypes.JAVA_CLASS_TYPE, returnType, iv) + val receiverValue = resolvedCall!!.extensionReceiver as? ExpressionReceiver ?: return null + val classLiteralExpression = receiverValue.expression as? KtClassLiteralExpression ?: return null + val receiverExpression = classLiteralExpression.receiverExpression ?: return null + val lhs = codegen.bindingContext.get(DOUBLE_COLON_LHS, receiverExpression) ?: return null + val value = codegen.generateClassLiteralReference(lhs, receiverExpression, /* wrapIntoKClass = */ false) + return StackValue.coercion(value, returnType) } } diff --git a/compiler/testData/codegen/box/classLiteral/bound/javaIntrinsicWithSideEffect.kt b/compiler/testData/codegen/box/classLiteral/bound/javaIntrinsicWithSideEffect.kt new file mode 100644 index 00000000000..780aaf76d52 --- /dev/null +++ b/compiler/testData/codegen/box/classLiteral/bound/javaIntrinsicWithSideEffect.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME + +fun box(): String { + var x = 42 + val k = (x++)::class.java + if (k != Int::class.java) return "Fail 1: $k" + if (x != 43) return "Fail 2: $x (side effect should have taken place)" + + return "OK" +} diff --git a/compiler/testData/codegen/box/classLiteral/bound/primitives.kt b/compiler/testData/codegen/box/classLiteral/bound/primitives.kt new file mode 100644 index 00000000000..943a11203bf --- /dev/null +++ b/compiler/testData/codegen/box/classLiteral/bound/primitives.kt @@ -0,0 +1,17 @@ +// WITH_RUNTIME + +import kotlin.test.assertEquals + +fun box(): String { + assertEquals(true::class, Boolean::class) + assertEquals(42.toByte()::class, Byte::class) + assertEquals('z'::class, Char::class) + assertEquals(3.14::class, Double::class) + assertEquals(2.72f::class, Float::class) + // TODO: KT-13110 + // assertEquals(42::class, Int::class) + assertEquals(42L::class, Long::class) + assertEquals(42.toShort()::class, Short::class) + + return "OK" +} diff --git a/compiler/testData/codegen/box/classLiteral/bound/sideEffect.kt b/compiler/testData/codegen/box/classLiteral/bound/sideEffect.kt new file mode 100644 index 00000000000..6c8fd3544a0 --- /dev/null +++ b/compiler/testData/codegen/box/classLiteral/bound/sideEffect.kt @@ -0,0 +1,14 @@ +fun box(): String { + var x = 42 + + val k1 = (x++)::class + if (k1 != Int::class) return "Fail 1: $k1" + if (x != 43) return "Fail 2: $x" + + val k2 = { x *= 2; x }()::class + // Note that k2 is the class of the wrapper type java.lang.Integer + if (k2 != Integer::class) return "Fail 3: $k2" + if (x != 86) return "Fail 4: $x" + + return "OK" +} diff --git a/compiler/testData/codegen/box/classLiteral/bound/simple.kt b/compiler/testData/codegen/box/classLiteral/bound/simple.kt new file mode 100644 index 00000000000..ac8884a72ed --- /dev/null +++ b/compiler/testData/codegen/box/classLiteral/bound/simple.kt @@ -0,0 +1,5 @@ +fun box(): String { + val x: CharSequence = "" + val klass = x::class + return if (klass == String::class) "OK" else "Fail: $klass" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 6fb20c3bb1a..9a38c2fae27 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -2342,6 +2342,48 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/classLiteral") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ClassLiteral extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInClassLiteral() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/classLiteral"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("compiler/testData/codegen/box/classLiteral/bound") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Bound extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInBound() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/classLiteral/bound"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("javaIntrinsicWithSideEffect.kt") + public void testJavaIntrinsicWithSideEffect() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/bound/javaIntrinsicWithSideEffect.kt"); + doTest(fileName); + } + + @TestMetadata("primitives.kt") + public void testPrimitives() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/bound/primitives.kt"); + doTest(fileName); + } + + @TestMetadata("sideEffect.kt") + public void testSideEffect() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/bound/sideEffect.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classLiteral/bound/simple.kt"); + doTest(fileName); + } + } + } + @TestMetadata("compiler/testData/codegen/box/classes") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)