Equality comparison for bound callable references takes into account bound receiver.
Fixed KT-14939: use expected receiver type when generating receiver code in get/set methods for bound property references.
Otherwise we have VerifyError for bound receiver 'null' of type 'Nothing?', which is mapped to 'java.lang.Void'.
TODO: proper equality comparison for property accessors ('x::prop.getter', 'x::prop.setter').
This commit is contained in:
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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<KtElement> {
|
||||
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<KtElement> {
|
||||
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<KtElement> {
|
||||
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<KtElement> {
|
||||
mv.visitCode();
|
||||
InstructionAdapter iv = new InstructionAdapter(mv);
|
||||
|
||||
int k = 1;
|
||||
for (FieldInfo fieldInfo : args) {
|
||||
k = genAssignInstanceFieldFromParam(fieldInfo, k, iv);
|
||||
Pair<Integer, Type> 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(), "<init>", "(I)V", false);
|
||||
if (shouldHaveBoundReferenceReceiver) {
|
||||
CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(iv, boundReferenceReceiverParameterIndex, boundReferenceReceiverType);
|
||||
superClassConstructorDescriptor = "(ILjava/lang/Object;)V";
|
||||
}
|
||||
else {
|
||||
superClassConstructorDescriptor = "(I)V";
|
||||
}
|
||||
}
|
||||
else {
|
||||
iv.invokespecial(superClassAsmType.getInternalName(), "<init>", "()V", false);
|
||||
assert !shouldHaveBoundReferenceReceiver : "Unexpected bound reference with supertype " + superClassAsmType;
|
||||
superClassConstructorDescriptor = "()V";
|
||||
}
|
||||
iv.invokespecial(superClassAsmType.getInternalName(), "<init>", superClassConstructorDescriptor, false);
|
||||
|
||||
iv.visitInsn(RETURN);
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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, "<init>", "()V", false)
|
||||
if (receiverIndexAndType == null) {
|
||||
assert(!shouldHaveBoundReferenceReceiver) { "No bound reference receiver in constructor parameters: $constructorArgs" }
|
||||
load(0, OBJECT_TYPE)
|
||||
invokespecial(superAsmType.internalName, "<init>", "()V", false)
|
||||
}
|
||||
else {
|
||||
val (receiverIndex, receiverType) = receiverIndexAndType
|
||||
load(0, OBJECT_TYPE)
|
||||
loadBoundReferenceReceiverParameter(receiverIndex, receiverType)
|
||||
invokespecial(superAsmType.internalName, "<init>", "(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)
|
||||
|
||||
@@ -29,6 +29,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface CalculatedClosure {
|
||||
@NotNull
|
||||
ClassDescriptor getClosureClass();
|
||||
|
||||
@Nullable
|
||||
ClassDescriptor getCaptureThis();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<FieldInfo>): Pair<Int, Type>? {
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user