Propagate KotlinType to callable reference receivers

This commit is contained in:
Dmitry Petrov
2018-10-15 16:38:56 +03:00
parent 3dc4d01adc
commit 9c7c0c8952
7 changed files with 125 additions and 56 deletions
@@ -438,13 +438,25 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv);
Pair<Integer, Type> receiverIndexAndType =
Pair<Integer, FieldInfo> receiverIndexAndFieldInfo =
CallableReferenceUtilKt.generateClosureFieldsInitializationFromParameters(iv, closure, args);
if (shouldHaveBoundReferenceReceiver && receiverIndexAndType == null) {
if (shouldHaveBoundReferenceReceiver && receiverIndexAndFieldInfo == null) {
throw new AssertionError("No bound reference receiver in constructor parameters: " + args);
}
int boundReferenceReceiverParameterIndex = shouldHaveBoundReferenceReceiver ? receiverIndexAndType.getFirst() : -1;
Type boundReferenceReceiverType = shouldHaveBoundReferenceReceiver ? receiverIndexAndType.getSecond() : null;
int boundReceiverParameterIndex;
Type boundReceiverType;
KotlinType boundReceiverKotlinType;
if (shouldHaveBoundReferenceReceiver) {
boundReceiverParameterIndex = receiverIndexAndFieldInfo.getFirst();
boundReceiverType = receiverIndexAndFieldInfo.getSecond().getFieldType();
boundReceiverKotlinType = receiverIndexAndFieldInfo.getSecond().getFieldKotlinType();
}
else {
boundReceiverParameterIndex = -1;
boundReceiverType = null;
boundReceiverKotlinType = null;
}
iv.load(0, superClassAsmType);
@@ -454,7 +466,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
int arity = calculateArity();
iv.iconst(arity);
if (shouldHaveBoundReferenceReceiver) {
CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(iv, boundReferenceReceiverParameterIndex, boundReferenceReceiverType);
CallableReferenceUtilKt.loadBoundReferenceReceiverParameter(iv, boundReceiverParameterIndex, boundReceiverType, boundReceiverKotlinType);
superClassConstructorDescriptor = "(ILjava/lang/Object;)V";
}
else {
@@ -3053,7 +3053,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
FunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
if (functionDescriptor != null) {
FunctionReferenceGenerationStrategy strategy = new FunctionReferenceGenerationStrategy(
state, functionDescriptor, resolvedCall, receiver != null ? receiver.type : null, null, false
state, functionDescriptor, resolvedCall,
receiver != null ? new JvmKotlinType(receiver.type, receiver.kotlinType) : null,
null, false
);
return genClosure(
@@ -3092,10 +3094,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
element.getContainingFile()
);
Type receiverAsmType = receiverValue != null ? receiverValue.type : null;
PropertyReferenceCodegen codegen = new PropertyReferenceCodegen(
state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION),
element, classBuilder, variableDescriptor, target, receiverAsmType
element, classBuilder, variableDescriptor, target,
receiverValue != null ? new JvmKotlinType(receiverValue.type, receiverValue.kotlinType) : null
);
codegen.generate();
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
@@ -51,6 +52,7 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
private final FunctionDescriptor referencedFunction;
private final FunctionDescriptor functionDescriptor;
private final Type receiverType; // non-null for bound references
private final KotlinType receiverKotlinType;
private final StackValue receiverValue;
private final boolean isInliningStrategy;
@@ -58,7 +60,7 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
@NotNull GenerationState state,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull ResolvedCall<?> resolvedCall,
@Nullable Type receiverType,
@Nullable JvmKotlinType receiverJvmKotlinType,
@Nullable StackValue receiverValue,
boolean isInliningStrategy
) {
@@ -73,7 +75,8 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
this.referencedFunction = referencedFunction;
this.functionDescriptor = functionDescriptor;
}
this.receiverType = receiverType;
this.receiverType = receiverJvmKotlinType != null ? receiverJvmKotlinType.getType() : null;
this.receiverKotlinType = receiverJvmKotlinType != null ? receiverJvmKotlinType.getKotlinType() : null;
this.receiverValue = receiverValue;
this.isInliningStrategy = isInliningStrategy;
assert receiverType != null || receiverValue == null
@@ -240,7 +243,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.capturedBoundReferenceReceiver(asmType, receiverType, isInliningStrategy);
return CallableReferenceUtilKt.capturedBoundReferenceReceiver(asmType, receiverType, receiverKotlinType, isInliningStrategy);
}
// 0 is this (the callable reference class), 1 is the invoke() method's first parameter
@@ -31,7 +31,9 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.PropertyImportedFromObject
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.isUnderlyingPropertyOfInlineClass
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
@@ -52,8 +54,11 @@ class PropertyReferenceCodegen(
classBuilder: ClassBuilder,
private val localVariableDescriptorForReference: VariableDescriptor,
private val target: VariableDescriptor,
private val receiverType: Type?
private val boundReceiverJvmKotlinType: JvmKotlinType?
) : MemberCodegen<KtElement>(state, parentCodegen, context, expression, classBuilder) {
private val boundReceiverType = boundReceiverJvmKotlinType?.type
private val classDescriptor = context.contextDescriptor
private val asmType = typeMapper.mapClass(classDescriptor)
@@ -72,9 +77,9 @@ class PropertyReferenceCodegen(
private val wrapperMethod = getWrapperMethodForPropertyReference(target, getFunction.valueParameters.size)
private val closure = bindingContext.get(CodegenBinding.CLOSURE, classDescriptor)!!.apply {
assert((capturedReceiverFromOuterContext != null) == (receiverType != null)) {
assert((capturedReceiverFromOuterContext != null) == (boundReceiverType != null)) {
"Bound property reference can only be generated with the type of the receiver. " +
"Captured type = $capturedReceiverFromOuterContext, actual type = $receiverType"
"Captured type = $capturedReceiverFromOuterContext, actual type = $boundReceiverType"
}
}
@@ -129,16 +134,16 @@ class PropertyReferenceCodegen(
private fun generateConstructor() {
generateMethod("property reference init", 0, constructor) {
val shouldHaveBoundReferenceReceiver = closure.isForBoundCallableReference()
val receiverIndexAndType = generateClosureFieldsInitializationFromParameters(closure, constructorArgs)
val receiverIndexAndFieldInfo = generateClosureFieldsInitializationFromParameters(closure, constructorArgs)
if (receiverIndexAndType == null) {
if (receiverIndexAndFieldInfo == 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
val (receiverIndex, receiverFieldInfo) = receiverIndexAndFieldInfo
load(0, OBJECT_TYPE)
loadBoundReferenceReceiverParameter(receiverIndex, receiverType)
loadBoundReferenceReceiverParameter(receiverIndex, receiverFieldInfo.fieldType, receiverFieldInfo.fieldKotlinType)
invokespecial(superAsmType.internalName, "<init>", "(Ljava/lang/Object;)V", false)
}
}
@@ -155,7 +160,7 @@ class PropertyReferenceCodegen(
getFunction,
target,
asmType,
receiverType,
boundReceiverJvmKotlinType,
element,
state,
false
@@ -176,7 +181,7 @@ class PropertyReferenceCodegen(
setFunction,
target,
asmType,
receiverType,
boundReceiverJvmKotlinType,
element,
state,
false
@@ -284,19 +289,39 @@ class PropertyReferenceCodegen(
}
class PropertyReferenceGenerationStrategy(
val isGetter: Boolean,
private val isGetter: Boolean,
private val originalFunctionDesc: FunctionDescriptor,
val target: VariableDescriptor,
val asmType: Type,
val receiverType: Type?,
val expression: KtElement,
private val target: VariableDescriptor,
private val asmType: Type,
boundReceiverJvmKotlinType: JvmKotlinType?,
private val expression: KtElement,
state: GenerationState,
private val isInliningStrategy: Boolean
) :
FunctionGenerationStrategy.CodegenBased(state) {
private val boundReceiverType = boundReceiverJvmKotlinType?.type
private val boundReceiverKotlinType = boundReceiverJvmKotlinType?.kotlinType
private val expectedReceiverKotlinType =
target.extensionReceiverParameter?.type
?: (target.containingDeclaration as? ClassDescriptor)?.defaultType
private val expectedReceiverType =
if (expectedReceiverKotlinType != null)
state.typeMapper.mapType(expectedReceiverKotlinType)
else {
assert(boundReceiverType == null) {
"$target: no expected receiver, boundReceiverType is $boundReceiverType"
}
null
}
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
val v = codegen.v
val typeMapper = state.typeMapper
val targetKotlinType = target.type
if (target is PropertyImportedFromObject) {
val containingObject = target.containingObject
StackValue
@@ -304,31 +329,51 @@ class PropertyReferenceCodegen(
.put(typeMapper.mapClass(containingObject), containingObject.defaultType, v)
}
if (receiverType != null) {
val expectedReceiver =
target.extensionReceiverParameter?.type ?: (target.containingDeclaration as? ClassDescriptor)?.defaultType
val expectedReceiverType = if (expectedReceiver != null) typeMapper.mapType(expectedReceiver) else receiverType
capturedBoundReferenceReceiver(asmType, expectedReceiverType, isInliningStrategy).put(expectedReceiverType, v)
if (boundReceiverType != null) {
capturedBoundReferenceReceiver(asmType, boundReceiverType, boundReceiverKotlinType, isInliningStrategy)
.put(expectedReceiverType!!, expectedReceiverKotlinType, v)
} else {
val receivers = originalFunctionDesc.valueParameters.dropLast(if (isGetter) 0 else 1)
receivers.forEachIndexed { i, valueParameterDescriptor ->
StackValue.local(i + 1, OBJECT_TYPE).put(typeMapper.mapType(valueParameterDescriptor), valueParameterDescriptor.type, v)
val nullableAny = valueParameterDescriptor.builtIns.nullableAnyType
StackValue.local(i + 1, OBJECT_TYPE, nullableAny)
.put(typeMapper.mapType(valueParameterDescriptor), valueParameterDescriptor.type, v)
}
}
val value = if (target is LocalVariableDescriptor) {
codegen.findLocalOrCapturedValue(target)!!
} else {
codegen.intermediateValueForProperty(target as PropertyDescriptor, false, null, StackValue.none())
val value = when {
target is LocalVariableDescriptor -> codegen.findLocalOrCapturedValue(target)!!
target.isUnderlyingPropertyOfInlineClass() -> {
if (expectedReceiverType == null)
throw AssertionError("$target: boundReceiverType=$boundReceiverType, expectedReceiverType is null")
val receiver =
if (boundReceiverType != null)
StackValue.onStack(expectedReceiverType, expectedReceiverKotlinType)
else
StackValue.none()
StackValue.underlyingValueOfInlineClass(typeMapper.mapType(targetKotlinType), targetKotlinType, receiver)
}
else -> codegen.intermediateValueForProperty(target as PropertyDescriptor, false, null, StackValue.none())
}
codegen.markStartLineNumber(expression)
val type = target.type
if (isGetter) {
value.put(OBJECT_TYPE, type, v)
value.put(OBJECT_TYPE, targetKotlinType, v)
} else {
val functionDescriptor = codegen.context.functionDescriptor
value.store(StackValue.local(codegen.frameMap.getIndex(functionDescriptor.valueParameters.last()), OBJECT_TYPE, type), v)
value.store(
StackValue.local(
codegen.frameMap.getIndex(
codegen.context.functionDescriptor.valueParameters.last()
),
OBJECT_TYPE, targetKotlinType
),
v
)
}
v.areturn(signature.returnType)
}
@@ -24,10 +24,17 @@ import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
fun capturedBoundReferenceReceiver(ownerType: Type, expectedReceiverType: Type, isInliningStrategy: Boolean): StackValue =
fun capturedBoundReferenceReceiver(
ownerType: Type,
expectedReceiverType: Type,
expectedReceiverKotlinType: KotlinType?,
isInliningStrategy: Boolean
): StackValue =
StackValue.operation(expectedReceiverType) { iv ->
iv.load(0, ownerType)
iv.getfield(
@@ -36,7 +43,8 @@ fun capturedBoundReferenceReceiver(ownerType: Type, expectedReceiverType: Type,
if (isInliningStrategy) AsmUtil.CAPTURED_RECEIVER_FIELD else AsmUtil.BOUND_REFERENCE_RECEIVER,
AsmTypes.OBJECT_TYPE.descriptor
)
StackValue.coerce(AsmTypes.OBJECT_TYPE, expectedReceiverType, iv)
val nullableAny = expectedReceiverKotlinType?.run { builtIns.nullableAnyType }
StackValue.coerce(AsmTypes.OBJECT_TYPE, nullableAny, expectedReceiverType, expectedReceiverKotlinType, iv)
}
fun ClassDescriptor.isSyntheticClassForCallableReference(): Boolean =
@@ -49,9 +57,10 @@ fun CalculatedClosure.isForCallableReference(): Boolean =
fun CalculatedClosure.isForBoundCallableReference(): Boolean =
isForCallableReference() && capturedReceiverFromOuterContext != null
fun InstructionAdapter.loadBoundReferenceReceiverParameter(index: Int, type: Type) {
fun InstructionAdapter.loadBoundReferenceReceiverParameter(index: Int, type: Type, kotlinType: KotlinType?) {
load(index, type)
StackValue.coerce(type, AsmTypes.OBJECT_TYPE, this)
val nullableAny = kotlinType?.run { builtIns.nullableAnyType }
StackValue.coerce(type, kotlinType, AsmTypes.OBJECT_TYPE, nullableAny, this)
}
fun CalculatedClosure.isBoundReferenceReceiverField(fieldInfo: FieldInfo): Boolean =
@@ -61,26 +70,21 @@ fun CalculatedClosure.isBoundReferenceReceiverField(fieldInfo: FieldInfo): Boole
fun InstructionAdapter.generateClosureFieldsInitializationFromParameters(
closure: CalculatedClosure,
args: List<FieldInfo>
): Pair<Int, Type>? {
): Pair<Int, FieldInfo>? {
var k = 1
var boundReferenceReceiverParameterIndex = -1
var boundReferenceReceiverType: Type? = null
var boundReferenceReceiverFieldInfo: FieldInfo? = null
for (fieldInfo in args) {
if (closure.isBoundReferenceReceiverField(fieldInfo)) {
boundReferenceReceiverParameterIndex = k
boundReferenceReceiverType = fieldInfo.fieldType
boundReferenceReceiverFieldInfo = fieldInfo
k += fieldInfo.fieldType.size
continue
}
k = AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, k, this)
}
return when {
boundReferenceReceiverType != null ->
Pair(boundReferenceReceiverParameterIndex, boundReferenceReceiverType)
else ->
null
}
return boundReferenceReceiverFieldInfo?.let { Pair(boundReferenceReceiverParameterIndex, it) }
}
fun computeExpectedNumberOfReceivers(referencedFunction: FunctionDescriptor, isBound: Boolean): Int {
@@ -172,17 +172,20 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
val strategy = when (expression) {
is KtCallableReferenceExpression -> {
val resolvedCall = expression.callableReference.getResolvedCallWithAssert(state.bindingContext)
val receiverType = JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)?.type?.let(state.typeMapper::mapType)
val receiverKotlinType = JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)?.type
val receiverType = receiverKotlinType?.let(state.typeMapper::mapType)
val boundReceiverJvmKotlinType = receiverType?.let { JvmKotlinType(receiverType, receiverKotlinType) }
if (isLambda && lambdaInfo!!.isPropertyReference) {
val asmType = state.typeMapper.mapClass(lambdaInfo.classDescriptor)
val info = lambdaInfo.propertyReferenceInfo
PropertyReferenceCodegen.PropertyReferenceGenerationStrategy(
true, info!!.getFunction, info.target, asmType, receiverType,
true, info!!.getFunction, info.target, asmType,
boundReceiverJvmKotlinType,
lambdaInfo.functionWithBodyOrCallableReference, state, true
)
} else {
FunctionReferenceGenerationStrategy(state, descriptor, resolvedCall, receiverType, null, true)
FunctionReferenceGenerationStrategy(state, descriptor, resolvedCall, boundReceiverJvmKotlinType, null, true)
}
}
is KtFunctionLiteral -> ClosureGenerationStrategy(state, expression as KtDeclarationWithBody)
@@ -58,7 +58,7 @@ fun KotlinType.isNullableUnderlyingType(): Boolean {
return TypeUtils.isNullableType(underlyingType)
}
fun PropertyDescriptor.isUnderlyingPropertyOfInlineClass(): Boolean {
fun VariableDescriptor.isUnderlyingPropertyOfInlineClass(): Boolean {
val containingDeclaration = this.containingDeclaration
if (!containingDeclaration.isInlineClass()) return false