diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index e4d604e4d18..a27919f5e20 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -431,7 +431,7 @@ public class AsmUtil { } KotlinType captureReceiverType = closure.getCaptureReceiverType(); - if (captureReceiverType != null) { + if (captureReceiverType != null && !CallableReferenceUtilKt.isForCallableReference(closure)) { allFields.add(Pair.create(CAPTURED_RECEIVER_FIELD, typeMapper.mapType(captureReceiverType))); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index 3ec7565827c..7c9f75f620d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.codegen; import com.google.common.collect.Lists; import com.intellij.util.ArrayUtil; +import kotlin.Pair; import kotlin.Unit; import kotlin.collections.CollectionsKt; import kotlin.jvm.functions.Function1; @@ -78,6 +79,7 @@ public class ClosureCodegen extends MemberCodegen { protected final CalculatedClosure closure; private final Type asmType; private final int visibilityFlag; + private final boolean shouldHaveBoundReferenceReceiver; private Method constructor; private Type superClassAsmType; @@ -126,6 +128,8 @@ public class ClosureCodegen extends MemberCodegen { this.closure = bindingContext.get(CLOSURE, classDescriptor); assert closure != null : "Closure must be calculated for class: " + classDescriptor; + this.shouldHaveBoundReferenceReceiver = CallableReferenceUtilKt.isForBoundCallableReference(closure); + this.asmType = typeMapper.mapClass(classDescriptor); visibilityFlag = AsmUtil.getVisibilityAccessFlagForClass(classDescriptor); @@ -185,7 +189,6 @@ public class ClosureCodegen extends MemberCodegen { protected void generateClosureBody() { functionCodegen.generateMethod(JvmDeclarationOriginKt.OtherOrigin(element, funDescriptor), funDescriptor, strategy); - if (functionReferenceTarget != null) { generateFunctionReferenceMethods(functionReferenceTarget); } @@ -415,23 +418,35 @@ public class ClosureCodegen extends MemberCodegen { mv.visitCode(); InstructionAdapter iv = new InstructionAdapter(mv); - int k = 1; - for (FieldInfo fieldInfo : args) { - k = genAssignInstanceFieldFromParam(fieldInfo, k, iv); + Pair receiverIndexAndType = + CallableReferenceUtilKt.generateClosureFieldsInitializationFromParameters(iv, closure, args); + if (shouldHaveBoundReferenceReceiver && receiverIndexAndType == null) { + throw new AssertionError("No bound reference receiver in constructor parameters: " + args); } + int boundReferenceReceiverParameterIndex = shouldHaveBoundReferenceReceiver ? receiverIndexAndType.getFirst() : -1; + Type boundReferenceReceiverType = shouldHaveBoundReferenceReceiver ? receiverIndexAndType.getSecond() : null; iv.load(0, superClassAsmType); + String superClassConstructorDescriptor; if (superClassAsmType.equals(LAMBDA) || superClassAsmType.equals(FUNCTION_REFERENCE) || superClassAsmType.equals(COROUTINE_IMPL)) { int arity = funDescriptor.getValueParameters().size(); if (funDescriptor.getExtensionReceiverParameter() != null) arity++; if (funDescriptor.getDispatchReceiverParameter() != null) arity++; iv.iconst(arity); - iv.invokespecial(superClassAsmType.getInternalName(), "", "(I)V", false); + if (shouldHaveBoundReferenceReceiver) { + CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(iv, boundReferenceReceiverParameterIndex, boundReferenceReceiverType); + superClassConstructorDescriptor = "(ILjava/lang/Object;)V"; + } + else { + superClassConstructorDescriptor = "(I)V"; + } } else { - iv.invokespecial(superClassAsmType.getInternalName(), "", "()V", false); + assert !shouldHaveBoundReferenceReceiver : "Unexpected bound reference with supertype " + superClassAsmType; + superClassConstructorDescriptor = "()V"; } + iv.invokespecial(superClassAsmType.getInternalName(), "", superClassConstructorDescriptor, false); iv.visitInsn(RETURN); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java index 2dfa0b48372..ebfc1a38305 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionReferenceGenerationStrategy.java @@ -187,7 +187,7 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat if (receiverType != null) { ClassDescriptor classDescriptor = (ClassDescriptor) codegen.getContext().getParentContext().getContextDescriptor(); Type asmType = codegen.getState().getTypeMapper().mapClass(classDescriptor); - return CallableReferenceUtilKt.capturedReceiver(asmType, receiverType); + return CallableReferenceUtilKt.capturedBoundReferenceReceiver(asmType, receiverType); } // 0 is this (the callable reference class), 1 is the invoke() method's first parameter diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt index fbed4f7e576..5a98f5ec66b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt @@ -126,13 +126,20 @@ class PropertyReferenceCodegen( private fun generateConstructor() { generateMethod("property reference init", 0, constructor) { - constructorArgs.fold(1) { - i, fieldInfo -> - AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, i, this) - } + val shouldHaveBoundReferenceReceiver = closure.isForBoundCallableReference() + val receiverIndexAndType = generateClosureFieldsInitializationFromParameters(closure, constructorArgs) - load(0, OBJECT_TYPE) - invokespecial(superAsmType.internalName, "", "()V", false) + if (receiverIndexAndType == null) { + assert(!shouldHaveBoundReferenceReceiver) { "No bound reference receiver in constructor parameters: $constructorArgs" } + load(0, OBJECT_TYPE) + invokespecial(superAsmType.internalName, "", "()V", false) + } + else { + val (receiverIndex, receiverType) = receiverIndexAndType + load(0, OBJECT_TYPE) + loadBoundReferenceReceiverParameter(receiverIndex, receiverType) + invokespecial(superAsmType.internalName, "", "(Ljava/lang/Object;)V", false) + } } } @@ -248,7 +255,10 @@ class PropertyReferenceCodegen( } if (receiverType != null) { - capturedReceiver(asmType, receiverType).put(receiverType, v) + val expectedReceiver = target.dispatchReceiverParameter ?: target.extensionReceiverParameter ?: + throw AssertionError("receiverType: $receiverType; no dispatch or extension receiver: $target") + val expectedReceiverType = typeMapper.mapType(expectedReceiver.type) + capturedBoundReferenceReceiver(asmType, expectedReceiverType).put(expectedReceiverType, v) } else { val receivers = originalFunctionDesc.valueParameters.dropLast(if (isGetter) 0 else 1) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java index 33d699ca47a..1031769e7ef 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CalculatedClosure.java @@ -29,6 +29,9 @@ import java.util.List; import java.util.Map; public interface CalculatedClosure { + @NotNull + ClassDescriptor getClosureClass(); + @Nullable ClassDescriptor getCaptureThis(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java index 922dadbf73b..72c185b140e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/MutableClosure.java @@ -29,6 +29,7 @@ import java.util.*; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.getDirectMember; public final class MutableClosure implements CalculatedClosure { + private final ClassDescriptor closureClass; private final ClassDescriptor enclosingClass; private final CallableDescriptor enclosingFunWithReceiverDescriptor; @@ -42,6 +43,7 @@ public final class MutableClosure implements CalculatedClosure { private boolean isCoroutine; MutableClosure(@NotNull ClassDescriptor classDescriptor, @Nullable ClassDescriptor enclosingClass) { + this.closureClass = classDescriptor; this.enclosingClass = enclosingClass; this.enclosingFunWithReceiverDescriptor = enclosingExtensionMemberForClass(classDescriptor); } @@ -58,6 +60,12 @@ public final class MutableClosure implements CalculatedClosure { return null; } + @NotNull + @Override + public ClassDescriptor getClosureClass() { + return closureClass; + } + @Nullable public ClassDescriptor getEnclosingClass() { return enclosingClass; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt index 6d40118ca53..338d18f4f16 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/callableReferenceUtil.kt @@ -16,8 +16,58 @@ package org.jetbrains.kotlin.codegen +import org.jetbrains.kotlin.codegen.binding.CalculatedClosure +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.psi.KtCallableReferenceExpression +import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter -fun capturedReceiver(ownerType: Type, capturedReceiverType: Type): StackValue.Field { - return StackValue.field(capturedReceiverType, ownerType, AsmUtil.CAPTURED_RECEIVER_FIELD, /* isStatic = */ false, StackValue.LOCAL_0) +fun capturedBoundReferenceReceiver(ownerType: Type, expectedReceiverType: Type): StackValue = + StackValue.operation(expectedReceiverType) { iv -> + iv.load(0, ownerType) + iv.getfield(ownerType.internalName, AsmUtil.CAPTURED_RECEIVER_FIELD, AsmTypes.OBJECT_TYPE.descriptor) + StackValue.coerce(AsmTypes.OBJECT_TYPE, expectedReceiverType, iv) + } + +fun ClassDescriptor.isSyntheticClassForCallableReference(): Boolean = + this is SyntheticClassDescriptorForLambda && + (this.source as? KotlinSourceElement)?.psi is KtCallableReferenceExpression + +fun CalculatedClosure.isForCallableReference(): Boolean = + closureClass.isSyntheticClassForCallableReference() + +fun CalculatedClosure.isForBoundCallableReference(): Boolean = + isForCallableReference() && captureReceiverType != null + +fun InstructionAdapter.loadBoundReferenceReceiverParameter(index: Int, type: Type) { + load(index, type) + StackValue.coerce(type, AsmTypes.OBJECT_TYPE, this) } + +fun CalculatedClosure.isBoundReferenceReceiverField(fieldInfo: FieldInfo): Boolean = + isForBoundCallableReference() && + fieldInfo.fieldName == AsmUtil.CAPTURED_RECEIVER_FIELD + +fun InstructionAdapter.generateClosureFieldsInitializationFromParameters(closure: CalculatedClosure, args: List): Pair? { + var k = 1 + var boundReferenceReceiverParameterIndex = -1 + var boundReferenceReceiverType: Type? = null + for (fieldInfo in args) { + if (closure.isBoundReferenceReceiverField(fieldInfo)) { + boundReferenceReceiverParameterIndex = k + boundReferenceReceiverType = fieldInfo.fieldType + k += fieldInfo.fieldType.size + continue + } + k = AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, k, this) + } + + return when { + boundReferenceReceiverType != null -> + Pair(boundReferenceReceiverParameterIndex, boundReferenceReceiverType) + else -> + null + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt b/compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt new file mode 100644 index 00000000000..ddf856c54da --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt @@ -0,0 +1,7 @@ +class A { + companion object { + fun ok() = "OK" + } +} + +fun box() = (A.Companion::ok)() \ No newline at end of file diff --git a/compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt b/compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt index 955164374ac..c4848e9ed44 100644 --- a/compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt +++ b/compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt @@ -9,8 +9,11 @@ enum class E { fun box(): String { val f = E.A::foo + val ef = E::foo + if (f() != "A") return "Fail 1: ${f()}" - if (f != E.B::foo) return "Fail 2" + if (f == E.B::foo) return "Fail 2" + if (ef != E::foo) return "Fail 3" return "OK" } diff --git a/compiler/testData/codegen/box/callableReference/bound/equals/nullableReceiverInEquals.kt b/compiler/testData/codegen/box/callableReference/bound/equals/nullableReceiverInEquals.kt new file mode 100644 index 00000000000..e0479cd1db4 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/bound/equals/nullableReceiverInEquals.kt @@ -0,0 +1,37 @@ +// TODO: investigate should it be ran for JS or not +// IGNORE_BACKEND: JS + +// See https://youtrack.jetbrains.com/issue/KT-14938 +// WITH_REFLECT + +class A + +val a = A() +val aa = A() + +fun A?.foo() {} + +var A?.bar: Int + get() = 42 + set(value) {} + +val aFoo = a::foo +val A_foo = A::foo +val nullFoo = null::foo + +val aBar = a::bar +val A_bar = A::bar +val nullBar = null::bar + +fun box(): String = + when { + nullFoo != null::foo -> "Bound extension refs with same receiver SHOULD be equal" + nullFoo == aFoo -> "Bound extension refs with different receivers SHOULD NOT be equal" + nullFoo == A_foo -> "Bound extension ref with receiver 'null' SHOULD NOT be equal to free ref" + + nullBar != null::bar -> "Bound extension property refs with same receiver SHOULD be equal" + nullBar == aBar -> "Bound extension property refs with different receivers SHOULD NOT be equal" + nullBar == A_bar -> "Bound extension property ref with receiver 'null' SHOULD NOT be equal to free property ref" + + else -> "OK" + } \ No newline at end of file diff --git a/compiler/testData/codegen/box/callableReference/bound/equals/receiverInEquals.kt b/compiler/testData/codegen/box/callableReference/bound/equals/receiverInEquals.kt new file mode 100644 index 00000000000..fd21eac125b --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/bound/equals/receiverInEquals.kt @@ -0,0 +1,27 @@ +// TODO: investigate should it be ran for JS or not +// IGNORE_BACKEND: JS + +package test + +class A { + fun foo() {} + fun bar() {} +} + +val a = A() +val aa = A() + +val aFoo = a::foo +val aBar = a::bar +val aaFoo = aa::foo +val A_foo = A::foo + +fun box(): String = + when { + aFoo != a::foo -> "Bound refs with same receiver SHOULD be equal" + aFoo == aBar -> "Bound refs to different members SHOULD NOT be equal" + aFoo == aaFoo -> "Bound refs with different receiver SHOULD NOT be equal" + aFoo == A_foo -> "Bound ref SHOULD NOT be equal to free ref" + + else -> "OK" + } \ No newline at end of file diff --git a/compiler/testData/codegen/box/callableReference/bound/nullReceiver.kt b/compiler/testData/codegen/box/callableReference/bound/nullReceiver.kt new file mode 100644 index 00000000000..cd2bee58f02 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/bound/nullReceiver.kt @@ -0,0 +1,9 @@ +// TODO: investigate should it be ran for JS or not +// IGNORE_BACKEND: JS + +// See https://youtrack.jetbrains.com/issue/KT-14939 + +val String?.ok: String + get() = "OK" + +fun box() = (null::ok).get() \ No newline at end of file diff --git a/compiler/testData/codegen/box/callableReference/bound/objectReceiver.kt b/compiler/testData/codegen/box/callableReference/bound/objectReceiver.kt new file mode 100644 index 00000000000..a03203792d5 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/bound/objectReceiver.kt @@ -0,0 +1,5 @@ +object Singleton { + fun ok() = "OK" +} + +fun box() = (Singleton::ok)() \ No newline at end of file diff --git a/compiler/testData/codegen/box/callableReference/bound/primitiveReceiver.kt b/compiler/testData/codegen/box/callableReference/bound/primitiveReceiver.kt new file mode 100644 index 00000000000..fec2d79a7e1 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/bound/primitiveReceiver.kt @@ -0,0 +1,29 @@ +// TODO: investigate should it be ran for JS or not +// IGNORE_BACKEND: JS + +fun Boolean.foo() = 1 +fun Byte.foo() = 2 +fun Short.foo() = 3 +fun Int.foo() = 4 +fun Long.foo() = 5 +fun Char.foo() = 6 +fun Float.foo() = 7 +fun Double.foo() = 8 + +fun testRef(name: String, f: () -> Int, expected: Int) { + val actual = f() + if (actual != expected) throw AssertionError("$name: $actual != $expected") +} + +fun box(): String { + testRef("Boolean", true::foo, 1) + testRef("Byte", 1.toByte()::foo, 2) + testRef("Short", 1.toShort()::foo, 3) + testRef("Int", 1::foo, 4) + testRef("Long", 1L::foo, 5) + testRef("Char", '1'::foo, 6) + testRef("Float", 1.0F::foo, 7) + testRef("Double", 1.0::foo, 8) + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.kt b/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.kt new file mode 100644 index 00000000000..61fddc4b17f --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.kt @@ -0,0 +1,9 @@ +class A { + fun foo() {} + val bar = 42 +} + +val aFoo = A()::foo +val aBar = A()::bar +val A_foo = A::foo +val A_bar = A::bar diff --git a/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.txt b/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.txt new file mode 100644 index 00000000000..c0694e36ebd --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.txt @@ -0,0 +1,66 @@ +@kotlin.Metadata +public final class A { + private final field bar: int + public method (): void + public final method foo(): void + public final method getBar(): int +} + +@kotlin.Metadata +final class NoReceiverInCallableReferenceClassesKt$A_bar$1 { + public final static field INSTANCE: kotlin.reflect.KProperty1 + static method (): void + method (): void + public @org.jetbrains.annotations.Nullable method get(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object + public method getName(): java.lang.String + public method getOwner(): kotlin.reflect.KDeclarationContainer + public method getSignature(): java.lang.String +} + +@kotlin.Metadata +final class NoReceiverInCallableReferenceClassesKt$A_foo$1 { + public final static field INSTANCE: NoReceiverInCallableReferenceClassesKt$A_foo$1 + inner class NoReceiverInCallableReferenceClassesKt$A_foo$1 + static method (): void + method (): void + public final method getName(): java.lang.String + public final method getOwner(): kotlin.reflect.KDeclarationContainer + public final method getSignature(): java.lang.String + public final method invoke(@org.jetbrains.annotations.NotNull p0: A): void + public synthetic method invoke(p0: java.lang.Object): java.lang.Object +} + +@kotlin.Metadata +final class NoReceiverInCallableReferenceClassesKt$aBar$1 { + method (p0: A): void + public @org.jetbrains.annotations.Nullable method get(): java.lang.Object + public method getName(): java.lang.String + public method getOwner(): kotlin.reflect.KDeclarationContainer + public method getSignature(): java.lang.String +} + +@kotlin.Metadata +final class NoReceiverInCallableReferenceClassesKt$aFoo$1 { + inner class NoReceiverInCallableReferenceClassesKt$aFoo$1 + method (p0: A): void + public final method getName(): java.lang.String + public final method getOwner(): kotlin.reflect.KDeclarationContainer + public final method getSignature(): java.lang.String + public synthetic method invoke(): java.lang.Object + public final method invoke(): void +} + +@kotlin.Metadata +public final class NoReceiverInCallableReferenceClassesKt { + private final static @org.jetbrains.annotations.NotNull field A_bar: kotlin.reflect.KProperty1 + private final static @org.jetbrains.annotations.NotNull field A_foo: kotlin.reflect.KFunction + private final static @org.jetbrains.annotations.NotNull field aBar: kotlin.reflect.KProperty0 + private final static @org.jetbrains.annotations.NotNull field aFoo: kotlin.reflect.KFunction + inner class NoReceiverInCallableReferenceClassesKt$A_foo$1 + inner class NoReceiverInCallableReferenceClassesKt$aFoo$1 + static method (): void + public final static @org.jetbrains.annotations.NotNull method getABar(): kotlin.reflect.KProperty0 + public final static @org.jetbrains.annotations.NotNull method getAFoo(): kotlin.reflect.KFunction + public final static @org.jetbrains.annotations.NotNull method getA_bar(): kotlin.reflect.KProperty1 + public final static @org.jetbrains.annotations.NotNull method getA_foo(): kotlin.reflect.KFunction +} \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index ebb9faf3ba4..64bb4126fb1 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -1501,6 +1501,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference/bound"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("companionObjectReceiver.kt") + public void testCompanionObjectReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt"); + doTest(fileName); + } + @TestMetadata("enumEntryMember.kt") public void testEnumEntryMember() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt"); @@ -1519,6 +1525,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("nullReceiver.kt") + public void testNullReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/nullReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("objectReceiver.kt") + public void testObjectReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/objectReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("primitiveReceiver.kt") + public void testPrimitiveReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/primitiveReceiver.kt"); + doTest(fileName); + } + @TestMetadata("simpleFunction.kt") public void testSimpleFunction() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/simpleFunction.kt"); @@ -1537,6 +1561,27 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("compiler/testData/codegen/box/callableReference/bound/equals") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Equals extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInEquals() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference/bound/equals"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("nullableReceiverInEquals.kt") + public void testNullableReceiverInEquals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/nullableReceiverInEquals.kt"); + doTest(fileName); + } + + @TestMetadata("receiverInEquals.kt") + public void testReceiverInEquals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/receiverInEquals.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/callableReference/bound/inline") @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 d1d868c7014..e87ad42f1bf 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -1501,6 +1501,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference/bound"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("companionObjectReceiver.kt") + public void testCompanionObjectReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt"); + doTest(fileName); + } + @TestMetadata("enumEntryMember.kt") public void testEnumEntryMember() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt"); @@ -1519,6 +1525,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("nullReceiver.kt") + public void testNullReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/nullReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("objectReceiver.kt") + public void testObjectReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/objectReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("primitiveReceiver.kt") + public void testPrimitiveReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/primitiveReceiver.kt"); + doTest(fileName); + } + @TestMetadata("simpleFunction.kt") public void testSimpleFunction() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/simpleFunction.kt"); @@ -1537,6 +1561,27 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("compiler/testData/codegen/box/callableReference/bound/equals") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Equals extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInEquals() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference/bound/equals"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("nullableReceiverInEquals.kt") + public void testNullableReceiverInEquals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/nullableReceiverInEquals.kt"); + doTest(fileName); + } + + @TestMetadata("receiverInEquals.kt") + public void testReceiverInEquals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/receiverInEquals.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/callableReference/bound/inline") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 3baf0e536b3..9b9061d06e0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -90,6 +90,12 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { doTest(fileName); } + @TestMetadata("noReceiverInCallableReferenceClasses.kt") + public void testNoReceiverInCallableReferenceClasses() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.kt"); + doTest(fileName); + } + @TestMetadata("noRemoveAtInReadOnly.kt") public void testNoRemoveAtInReadOnly() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeListing/noRemoveAtInReadOnly.kt"); diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java index 16e6024d28c..3e4eca05669 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/CallableReference.java @@ -39,6 +39,17 @@ public abstract class CallableReference implements KCallable { // objects are written to it. The latter is guaranteed because both KFunctionImpl and KPropertyImpl have at least one final field. private KCallable reflected; + protected final Object receiver$0; + private static final Object NO_RECEIVER = new Object(); + + public CallableReference() { + this(NO_RECEIVER); + } + + protected CallableReference(Object receiver$0) { + this.receiver$0 = receiver$0; + } + protected abstract KCallable computeReflected(); public KCallable compute() { diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java index 7dd73687bc6..2bb4b8a0b30 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/FunctionReference.java @@ -26,6 +26,11 @@ public class FunctionReference extends CallableReference implements FunctionImpl this.arity = arity; } + public FunctionReference(int arity, Object receiver) { + super(receiver); + this.arity = arity; + } + @Override public int getArity() { return arity; @@ -71,9 +76,11 @@ public class FunctionReference extends CallableReference implements FunctionImpl if (obj == this) return true; if (obj instanceof FunctionReference) { FunctionReference other = (FunctionReference) obj; + return getOwner().equals(other.getOwner()) && getName().equals(other.getName()) && - getSignature().equals(other.getSignature()); + getSignature().equals(other.getSignature()) && + Intrinsics.areEqual(receiver$0, other.receiver$0); } if (obj instanceof KFunction) { return obj.equals(compute()); diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference.java index b5c4b6b7771..cc43d50dc9b 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference.java @@ -19,4 +19,10 @@ package kotlin.jvm.internal; import kotlin.reflect.KMutableProperty; public abstract class MutablePropertyReference extends PropertyReference implements KMutableProperty { + public MutablePropertyReference() { + } + + public MutablePropertyReference(Object receiver$0) { + super(receiver$0); + } } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference0.java b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference0.java index 51f6b5bdffd..ca397d41f7a 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference0.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference0.java @@ -21,6 +21,13 @@ import kotlin.reflect.KMutableProperty0; import kotlin.reflect.KProperty0; public abstract class MutablePropertyReference0 extends MutablePropertyReference implements KMutableProperty0 { + public MutablePropertyReference0() { + } + + public MutablePropertyReference0(Object receiver$0) { + super(receiver$0); + } + @Override protected KCallable computeReflected() { return Reflection.mutableProperty0(this); diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference1.java b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference1.java index 876a1f2addf..d3c335d87b2 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference1.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/MutablePropertyReference1.java @@ -21,6 +21,13 @@ import kotlin.reflect.KMutableProperty1; import kotlin.reflect.KProperty1; public abstract class MutablePropertyReference1 extends MutablePropertyReference implements KMutableProperty1 { + public MutablePropertyReference1() { + } + + public MutablePropertyReference1(Object receiver$0) { + super(receiver$0); + } + @Override protected KCallable computeReflected() { return Reflection.mutableProperty1(this); diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference.java b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference.java index 71512db7ba0..ad72643e5ec 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference.java @@ -20,6 +20,14 @@ import kotlin.reflect.KCallable; import kotlin.reflect.KProperty; public abstract class PropertyReference extends CallableReference implements KProperty { + public PropertyReference() { + super(); + } + + public PropertyReference(Object receiver$0) { + super(receiver$0); + } + @Override protected KProperty getReflected() { return (KProperty) super.getReflected(); @@ -42,7 +50,8 @@ public abstract class PropertyReference extends CallableReference implements KPr PropertyReference other = (PropertyReference) obj; return getOwner().equals(other.getOwner()) && getName().equals(other.getName()) && - getSignature().equals(other.getSignature()); + getSignature().equals(other.getSignature()) && + Intrinsics.areEqual(receiver$0, other.receiver$0); } if (obj instanceof KProperty) { return obj.equals(compute()); diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference0.java b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference0.java index 66c35048edd..b7dd0bfddf4 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference0.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference0.java @@ -20,6 +20,14 @@ import kotlin.reflect.KCallable; import kotlin.reflect.KProperty0; public abstract class PropertyReference0 extends PropertyReference implements KProperty0 { + public PropertyReference0() { + super(); + } + + public PropertyReference0(Object receiver$0) { + super(receiver$0); + } + @Override protected KCallable computeReflected() { return Reflection.property0(this); diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference1.java b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference1.java index 8e7aeb597b0..e113e94a11b 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference1.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/PropertyReference1.java @@ -20,6 +20,13 @@ import kotlin.reflect.KCallable; import kotlin.reflect.KProperty1; public abstract class PropertyReference1 extends PropertyReference implements KProperty1 { + public PropertyReference1() { + } + + public PropertyReference1(Object receiver$0) { + super(receiver$0); + } + @Override protected KCallable computeReflected() { return Reflection.property1(this); 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 3cbb37e84f6..90b82af729c 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 @@ -1874,6 +1874,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference/bound"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("companionObjectReceiver.kt") + public void testCompanionObjectReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt"); + doTest(fileName); + } + @TestMetadata("enumEntryMember.kt") public void testEnumEntryMember() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/enumEntryMember.kt"); @@ -1910,6 +1916,36 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("nullReceiver.kt") + public void testNullReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/nullReceiver.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("objectReceiver.kt") + public void testObjectReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/objectReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("primitiveReceiver.kt") + public void testPrimitiveReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/primitiveReceiver.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + @TestMetadata("simpleFunction.kt") public void testSimpleFunction() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/simpleFunction.kt"); @@ -1946,6 +1982,39 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("compiler/testData/codegen/box/callableReference/bound/equals") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Equals extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInEquals() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference/bound/equals"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("nullableReceiverInEquals.kt") + public void testNullableReceiverInEquals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/nullableReceiverInEquals.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + + @TestMetadata("receiverInEquals.kt") + public void testReceiverInEquals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/equals/receiverInEquals.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + } + @TestMetadata("compiler/testData/codegen/box/callableReference/bound/inline") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)