Support bound callable references in codegen

This commit is contained in:
Alexander Udalov
2016-04-29 21:59:27 +03:00
parent 526a64dc36
commit b9e61f035f
13 changed files with 242 additions and 65 deletions
@@ -241,7 +241,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
}
@NotNull
public StackValue putInstanceOnStack(@NotNull final ExpressionCodegen codegen) {
public StackValue putInstanceOnStack(@NotNull final ExpressionCodegen codegen, @Nullable final StackValue functionReferenceReceiver) {
return StackValue.operation(
functionReferenceTarget != null ? K_FUNCTION : asmType,
new Function1<InstructionAdapter, Unit>() {
@@ -254,7 +254,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
v.anew(asmType);
v.dup();
codegen.pushClosureOnStack(classDescriptor, true, codegen.defaultCallGenerator);
codegen.pushClosureOnStack(classDescriptor, true, codegen.defaultCallGenerator, functionReferenceReceiver);
v.invokespecial(asmType.getInternalName(), "<init>", constructor.getDescriptor(), false);
}
@@ -28,6 +28,7 @@ import com.intellij.util.containers.Stack;
import kotlin.Pair;
import kotlin.Unit;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -1418,7 +1419,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
assert descriptor != null : "Function is not resolved to descriptor: " + declaration.getText();
return genClosure(
declaration, descriptor, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), samType, null
declaration, descriptor, new FunctionGenerationStrategy.FunctionDefault(state, descriptor, declaration), samType, null, null
);
}
@@ -1428,7 +1429,8 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@NotNull FunctionDescriptor descriptor,
@NotNull FunctionGenerationStrategy strategy,
@Nullable SamType samType,
@Nullable FunctionDescriptor functionReferenceTarget
@Nullable FunctionDescriptor functionReferenceTarget,
@Nullable StackValue functionReferenceReceiver
) {
ClassBuilder cv = state.getFactory().newVisitor(
JvmDeclarationOriginKt.OtherOrigin(declaration, descriptor),
@@ -1448,7 +1450,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
propagateChildReifiedTypeParametersUsages(closureCodegen.getReifiedTypeParametersUsages());
}
return closureCodegen.putInstanceOnStack(this);
return closureCodegen.putInstanceOnStack(this, functionReferenceReceiver);
}
@Override
@@ -1466,7 +1468,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
v.anew(type);
v.dup();
pushClosureOnStack(classDescriptor, true, defaultCallGenerator);
pushClosureOnStack(classDescriptor, true, defaultCallGenerator, /* functionReferenceReceiver = */ null);
ConstructorDescriptor primaryConstructor = classDescriptor.getUnsubstitutedPrimaryConstructor();
assert primaryConstructor != null : "There should be primary constructor for object literal";
@@ -1510,7 +1512,12 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
});
}
public void pushClosureOnStack(@NotNull ClassDescriptor classDescriptor, boolean putThis, @NotNull CallGenerator callGenerator) {
public void pushClosureOnStack(
@NotNull ClassDescriptor classDescriptor,
boolean putThis,
@NotNull CallGenerator callGenerator,
@Nullable StackValue functionReferenceReceiver
) {
CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor);
if (closure == null) return;
@@ -1528,7 +1535,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
KotlinType captureReceiver = closure.getCaptureReceiverType();
if (captureReceiver != null) {
Type asmType = typeMapper.mapType(captureReceiver);
StackValue.Local capturedReceiver = StackValue.local(AsmUtil.getReceiverIndex(context, context.getContextDescriptor()), asmType);
StackValue capturedReceiver =
functionReferenceReceiver != null ? functionReferenceReceiver :
StackValue.local(AsmUtil.getReceiverIndex(context, context.getContextDescriptor()), asmType);
callGenerator.putCapturedValueOnStack(capturedReceiver, capturedReceiver.type, paramIndex++);
}
@@ -1545,9 +1554,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
ClassDescriptor superClass = DescriptorUtilsKt.getSuperClassNotAny(classDescriptor);
if (superClass != null) {
pushClosureOnStack(
superClass,
putThis && closure.getCaptureThis() == null,
callGenerator
superClass, putThis && closure.getCaptureThis() == null, callGenerator, /* functionReferenceReceiver = */ null
);
}
}
@@ -2852,17 +2859,28 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@Override
public StackValue visitCallableReferenceExpression(@NotNull KtCallableReferenceExpression expression, StackValue data) {
ResolvedCall<?> resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression.getCallableReference(), bindingContext);
KotlinType receiverExpressionType = expressionJetType(expression.getReceiverExpression());
Type receiverAsmType = receiverExpressionType != null ? asmType(receiverExpressionType) : null;
StackValue receiverValue = receiverExpressionType != null ? gen(expression.getReceiverExpression()) : null;
FunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
if (functionDescriptor != null) {
FunctionReferenceGenerationStrategy strategy = new FunctionReferenceGenerationStrategy(state, functionDescriptor, resolvedCall);
return genClosure(expression, functionDescriptor, strategy, null, (FunctionDescriptor) resolvedCall.getResultingDescriptor());
FunctionReferenceGenerationStrategy strategy =
new FunctionReferenceGenerationStrategy(state, functionDescriptor, resolvedCall, receiverAsmType, null);
return genClosure(
expression, functionDescriptor, strategy, null,
(FunctionDescriptor) resolvedCall.getResultingDescriptor(), receiverValue
);
}
VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
if (variableDescriptor != null) {
return generatePropertyReference(expression, variableDescriptor,
(VariableDescriptor) resolvedCall.getResultingDescriptor(),
resolvedCall.getDispatchReceiver());
return generatePropertyReference(
expression, variableDescriptor, (VariableDescriptor) resolvedCall.getResultingDescriptor(),
resolvedCall.getDispatchReceiver(), receiverAsmType, receiverValue
);
}
throw new UnsupportedOperationException("Unsupported callable reference expression: " + expression.getText());
@@ -2873,7 +2891,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@NotNull KtElement element,
@NotNull VariableDescriptor variableDescriptor,
@NotNull VariableDescriptor target,
@Nullable ReceiverValue dispatchReceiver
@Nullable ReceiverValue dispatchReceiver,
@Nullable final Type receiverAsmType,
@Nullable final StackValue receiverValue
) {
ClassDescriptor classDescriptor = CodegenBinding.anonymousClassForCallable(bindingContext, variableDescriptor);
@@ -2885,11 +2905,18 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
PropertyReferenceCodegen codegen = new PropertyReferenceCodegen(
state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION),
element, classBuilder, target, dispatchReceiver
element, classBuilder, target, dispatchReceiver, receiverAsmType
);
codegen.generate();
return codegen.putInstanceOnStack();
return codegen.putInstanceOnStack(receiverValue == null ? null : new Function0<Unit>() {
@Override
public Unit invoke() {
assert receiverAsmType != null : "Receiver type should not be null when receiver value is not null: " + receiverValue;
receiverValue.put(receiverAsmType, v);
return Unit.INSTANCE;
}
});
}
@NotNull
@@ -3524,8 +3551,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@NotNull StackValue metadataVar
) {
//noinspection ConstantConditions
StackValue value =
generatePropertyReference(variable.getDelegate(), variableDescriptor, variableDescriptor, null);
StackValue value = generatePropertyReference(variable.getDelegate(), variableDescriptor, variableDescriptor, null, null, null);
value.put(K_PROPERTY0_TYPE, v);
metadataVar.storeSelector(K_PROPERTY0_TYPE, v);
}
@@ -3570,7 +3596,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
// Resolved call to local class constructor doesn't have dispatchReceiver, so we need to generate closure on stack
// See StackValue.receiver for more info
pushClosureOnStack(containingDeclaration, dispatchReceiver == null, defaultCallGenerator);
pushClosureOnStack(
containingDeclaration, dispatchReceiver == null, defaultCallGenerator, /* functionReferenceReceiver = */ null
);
constructor = SamCodegenUtil.resolveSamAdapter(constructor);
CallableMethod method = typeMapper.mapToCallableMethod(constructor, false);
@@ -39,15 +39,24 @@ import java.util.*;
public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrategy.CodegenBased<FunctionDescriptor> {
private final ResolvedCall<?> resolvedCall;
private final FunctionDescriptor referencedFunction;
private final Type receiverType; // non-null for bound references
private final StackValue receiverValue;
public FunctionReferenceGenerationStrategy(
@NotNull GenerationState state,
@NotNull FunctionDescriptor functionDescriptor,
@NotNull ResolvedCall<?> resolvedCall
@NotNull ResolvedCall<?> resolvedCall,
@Nullable Type receiverType,
@Nullable StackValue receiverValue
) {
super(state, functionDescriptor);
this.resolvedCall = resolvedCall;
this.referencedFunction = (FunctionDescriptor) resolvedCall.getResultingDescriptor();
this.receiverType = receiverType;
this.receiverValue = receiverValue;
assert receiverType != null || receiverValue == null
: "A receiver value is provided for unbound function reference. Either this is a bound reference and you forgot " +
"to pass receiverType, or you accidentally passed some receiverValue for a reference without receiver";
}
@Override
@@ -142,7 +151,8 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
private void computeAndSaveArguments(@NotNull List<? extends ValueArgument> fakeArguments, @NotNull ExpressionCodegen codegen) {
int receivers = (referencedFunction.getDispatchReceiverParameter() != null ? 1 : 0) +
(referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0);
(referencedFunction.getExtensionReceiverParameter() != null ? 1 : 0) -
(receiverType != null ? 1 : 0);
List<ValueParameterDescriptor> parameters = CollectionsKt.drop(callableDescriptor.getValueParameters(), receivers);
for (int i = 0; i < parameters.size(); i++) {
@@ -163,14 +173,22 @@ public class FunctionReferenceGenerationStrategy extends FunctionGenerationStrat
) {
if (receiver == null) return null;
KtExpression receiverExpression = KtPsiFactoryKt
.KtPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature));
KtExpression receiverExpression = KtPsiFactoryKt.KtPsiFactory(state.getProject()).createExpression("callableReferenceFakeReceiver");
codegen.tempVariables.put(receiverExpression, receiverParameterStackValue(signature, codegen));
return ExpressionReceiver.Companion.create(receiverExpression, receiver.getType(), BindingContext.EMPTY);
}
@NotNull
private static StackValue.Local receiverParameterStackValue(@NotNull JvmMethodSignature signature) {
private StackValue receiverParameterStackValue(@NotNull JvmMethodSignature signature, @NotNull ExpressionCodegen codegen) {
if (receiverValue != null) return receiverValue;
if (receiverType != null) {
return StackValue.field(
receiverType, Type.getObjectType(codegen.getParentCodegen().getClassName()), AsmUtil.CAPTURED_RECEIVER_FIELD,
/* isStatic = */ false, StackValue.LOCAL_0
);
}
// 0 is this (the callable reference class), 1 is the invoke() method's first parameter
return StackValue.local(1, signature.getAsmMethod().getArgumentTypes()[0]);
}
@@ -95,7 +95,7 @@ public class JvmRuntimeTypes {
}
@NotNull
public Collection<KotlinType> getSupertypesForFunctionReference(@NotNull FunctionDescriptor descriptor) {
public Collection<KotlinType> getSupertypesForFunctionReference(@NotNull FunctionDescriptor descriptor, boolean isBound) {
ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
ReceiverParameterDescriptor dispatchReceiver = descriptor.getDispatchReceiverParameter();
@@ -106,7 +106,7 @@ public class JvmRuntimeTypes {
KotlinType functionType = FunctionTypeResolveUtilsKt.createFunctionType(
DescriptorUtilsKt.getBuiltIns(descriptor),
Annotations.Companion.getEMPTY(),
receiverType,
isBound ? null : receiverType,
ExpressionTypingUtils.getValueParametersTypes(descriptor.getValueParameters()),
descriptor.getReturnType()
);
@@ -115,13 +115,14 @@ public class JvmRuntimeTypes {
}
@NotNull
public KotlinType getSupertypeForPropertyReference(@NotNull VariableDescriptorWithAccessors descriptor) {
public KotlinType getSupertypeForPropertyReference(@NotNull VariableDescriptorWithAccessors descriptor, boolean isBound) {
if (descriptor instanceof LocalVariableDescriptor) {
return (descriptor.isVar() ? mutableLocalVariableReference : localVariableReference).getDefaultType();
}
int arity = (descriptor.getExtensionReceiverParameter() != null ? 1 : 0) +
(descriptor.getDispatchReceiverParameter() != null ? 1 : 0);
(descriptor.getDispatchReceiverParameter() != null ? 1 : 0) -
(isBound ? 1 : 0);
return (descriptor.isVar() ? mutablePropertyReferences : propertyReferences).get(arity).getDefaultType();
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.AsmUtil.method
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.context.ClassContext
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.*
@@ -46,7 +47,8 @@ class PropertyReferenceCodegen(
expression: KtElement,
classBuilder: ClassBuilder,
private val target: VariableDescriptor,
dispatchReceiver: ReceiverValue?
dispatchReceiver: ReceiverValue?,
private val receiverType: Type? // non-null for bound references
) : MemberCodegen<KtElement>(state, parentCodegen, context, expression, classBuilder) {
private val classDescriptor = context.contextDescriptor
private val asmType = typeMapper.mapClass(classDescriptor)
@@ -55,7 +57,9 @@ class PropertyReferenceCodegen(
private val extensionReceiverType = target.extensionReceiverParameter?.type
private val receiverCount =
(if (dispatchReceiverType != null) 1 else 0) + (if (extensionReceiverType != null) 1 else 0)
(if (dispatchReceiverType != null) 1 else 0) +
(if (extensionReceiverType != null) 1 else 0) -
(if (receiverType != null) 1 else 0)
// e.g. MutablePropertyReference0
private val superAsmType = typeMapper.mapClass(classDescriptor.getSuperClassNotAny().sure { "No super class for $classDescriptor" })
@@ -63,6 +67,18 @@ class PropertyReferenceCodegen(
// e.g. mutableProperty0(Lkotlin/jvm/internal/MutablePropertyReference0;)Lkotlin/reflect/KMutableProperty0;
private val wrapperMethod = getWrapperMethodForPropertyReference(target, receiverCount)
private val closure = bindingContext.get(CodegenBinding.CLOSURE, classDescriptor)!!.apply {
assert((captureReceiverType != null) == (receiverType != null)) {
"Bound property reference can only be generated with the type of the receiver. " +
"Captured type = $captureReceiverType, actual type = $receiverType"
}
}
private val constructorArgs = ClosureCodegen.calculateConstructorParameters(typeMapper, closure, asmType).apply {
assert(size <= 1) { "Bound property reference should capture only one value: $this" }
}
private val constructor = method("<init>", Type.VOID_TYPE, *constructorArgs.map { it.fieldType }.toTypedArray())
override fun generateDeclaration() {
v.defineClass(
element,
@@ -79,12 +95,14 @@ class PropertyReferenceCodegen(
// TODO: ImplementationBodyCodegen.markLineNumberForSyntheticFunction?
override fun generateBody() {
generateConstInstance(asmType, wrapperMethod.returnType)
generateMethod("property reference init", 0, method("<init>", Type.VOID_TYPE)) {
load(0, OBJECT_TYPE)
invokespecial(superAsmType.internalName, "<init>", "()V", false)
if (JvmCodegenUtil.isConst(closure)) {
generateConstInstance(asmType, wrapperMethod.returnType)
}
else {
AsmUtil.genClosureFields(closure, v, typeMapper)
}
generateConstructor()
generateMethod("property reference getName", ACC_PUBLIC, method("getName", JAVA_STRING_TYPE)) {
aconst(target.name.asString())
@@ -102,6 +120,18 @@ class PropertyReferenceCodegen(
}
}
private fun generateConstructor() {
generateMethod("property reference init", 0, constructor) {
constructorArgs.fold(1) {
i, fieldInfo ->
AsmUtil.genAssignInstanceFieldFromParam(fieldInfo, i, this)
}
load(0, OBJECT_TYPE)
invokespecial(superAsmType.internalName, "<init>", "()V", false)
}
}
private fun generateAccessors() {
fun generateAccessor(method: Method, accessorBody: InstructionAdapter.(StackValue) -> Unit) {
generateMethod("property reference $method", ACC_PUBLIC, method) {
@@ -123,8 +153,14 @@ class PropertyReferenceCodegen(
StackValue.singleton(containingObject, typeMapper).put(typeMapper.mapClass(containingObject), this)
}
for ((index, type) in listOfNotNull(dispatchReceiverType, extensionReceiverType).withIndex()) {
StackValue.local(index + 1, OBJECT_TYPE).put(typeMapper.mapType(type), this)
if (receiverType != null) {
StackValue.field(receiverType, asmType, AsmUtil.CAPTURED_RECEIVER_FIELD, /* isStatic = */ false, StackValue.LOCAL_0)
.put(receiverType, this)
}
else {
for ((index, type) in listOfNotNull(dispatchReceiverType, extensionReceiverType).withIndex()) {
StackValue.local(index + 1, OBJECT_TYPE).put(typeMapper.mapType(type), this)
}
}
val value = if (target is LocalVariableDescriptor) {
@@ -166,10 +202,21 @@ class PropertyReferenceCodegen(
writeSyntheticClassMetadata(v)
}
fun putInstanceOnStack(): StackValue =
StackValue.operation(wrapperMethod.returnType) { iv ->
fun putInstanceOnStack(receiverValue: (() -> Unit)?): StackValue {
return StackValue.operation(wrapperMethod.returnType) { iv ->
if (JvmCodegenUtil.isConst(closure)) {
assert(receiverValue == null) { "No receiver expected for unbound property reference: $classDescriptor" }
iv.getstatic(asmType.internalName, JvmAbi.INSTANCE_FIELD, wrapperMethod.returnType.descriptor)
}
else {
assert(receiverValue != null) { "Receiver expected for bound property reference: $classDescriptor" }
iv.anew(asmType)
iv.dup()
receiverValue!!()
iv.invokespecial(asmType.internalName, "<init>", constructor.descriptor, false)
}
}
}
companion object {
@JvmStatic
@@ -288,17 +288,22 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
CallableDescriptor callableDescriptor;
Collection<KotlinType> supertypes;
KtExpression receiverExpression = expression.getReceiverExpression();
KotlinType receiverType = receiverExpression != null ? bindingContext.getType(receiverExpression) : null;
if (target instanceof FunctionDescriptor) {
callableDescriptor = bindingContext.get(FUNCTION, expression);
if (callableDescriptor == null) return;
supertypes = runtimeTypes.getSupertypesForFunctionReference((FunctionDescriptor) target);
supertypes = runtimeTypes.getSupertypesForFunctionReference((FunctionDescriptor) target, receiverType != null);
}
else if (target instanceof PropertyDescriptor) {
callableDescriptor = bindingContext.get(VARIABLE, expression);
if (callableDescriptor == null) return;
supertypes = Collections.singleton(runtimeTypes.getSupertypeForPropertyReference((PropertyDescriptor) target));
supertypes = Collections.singleton(
runtimeTypes.getSupertypeForPropertyReference((PropertyDescriptor) target, receiverType != null)
);
}
else {
return;
@@ -306,7 +311,11 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
String name = inventAnonymousClassName();
ClassDescriptor classDescriptor = recordClassForCallable(expression, callableDescriptor, supertypes, name);
recordClosure(classDescriptor, name);
MutableClosure closure = recordClosure(classDescriptor, name);
if (receiverType != null) {
closure.setCaptureReceiverType(receiverType);
}
classStack.push(classDescriptor);
nameStack.push(name);
@@ -315,9 +324,11 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
classStack.pop();
}
private void recordClosure(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
CodegenBinding.recordClosure(bindingTrace, classDescriptor, peekFromStack(classStack), Type.getObjectType(name),
fileClassesProvider);
@NotNull
private MutableClosure recordClosure(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
return CodegenBinding.recordClosure(
bindingTrace, classDescriptor, peekFromStack(classStack), Type.getObjectType(name), fileClassesProvider
);
}
private void recordLocalVariablePropertyMetadata(LocalVariableDescriptor variableDescriptor) {
@@ -369,7 +380,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
if (delegate != null && descriptor instanceof VariableDescriptorWithAccessors) {
VariableDescriptorWithAccessors variableDescriptor = (VariableDescriptorWithAccessors) descriptor;
String name = inventAnonymousClassName();
KotlinType supertype = runtimeTypes.getSupertypeForPropertyReference(variableDescriptor);
KotlinType supertype = runtimeTypes.getSupertypeForPropertyReference(variableDescriptor, /* bound = */ false);
ClassDescriptor classDescriptor = recordClassForCallable(delegate, variableDescriptor, Collections.singleton(supertype), name);
recordClosure(classDescriptor, name);
}
@@ -139,7 +139,8 @@ public class CodegenBinding {
return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
}
static void recordClosure(
@NotNull
static MutableClosure recordClosure(
@NotNull BindingTrace trace,
@NotNull ClassDescriptor classDescriptor,
@Nullable ClassDescriptor enclosing,
@@ -164,6 +165,8 @@ public class CodegenBinding {
if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
recordInnerClass(trace, enclosing, classDescriptor);
}
return closure;
}
private static void recordInnerClass(
@@ -33,11 +33,12 @@ public final class MutableClosure implements CalculatedClosure {
private final CallableDescriptor enclosingFunWithReceiverDescriptor;
private boolean captureThis;
private boolean captureReceiver;
private boolean captureEnclosingReceiver;
private Map<DeclarationDescriptor, EnclosedValueDescriptor> captureVariables;
private Map<DeclarationDescriptor, Integer> parameterOffsetInConstructor;
private List<Pair<String, Type>> recordedFields;
private KotlinType captureReceiverType;
MutableClosure(@NotNull ClassDescriptor classDescriptor, @Nullable ClassDescriptor enclosingClass) {
this.enclosingClass = enclosingClass;
@@ -72,7 +73,11 @@ public final class MutableClosure implements CalculatedClosure {
@Override
public KotlinType getCaptureReceiverType() {
if (captureReceiver) {
if (captureReceiverType != null) {
return captureReceiverType;
}
if (captureEnclosingReceiver) {
ReceiverParameterDescriptor parameter = getEnclosingReceiverDescriptor();
assert parameter != null : "Receiver parameter should exist in " + enclosingFunWithReceiverDescriptor;
return parameter.getType();
@@ -85,7 +90,7 @@ public final class MutableClosure implements CalculatedClosure {
if (enclosingFunWithReceiverDescriptor == null) {
throw new IllegalStateException("Extension receiver parameter should exist");
}
this.captureReceiver = true;
this.captureEnclosingReceiver = true;
}
@NotNull
@@ -94,6 +99,10 @@ public final class MutableClosure implements CalculatedClosure {
return captureVariables != null ? captureVariables : Collections.<DeclarationDescriptor, EnclosedValueDescriptor>emptyMap();
}
public void setCaptureReceiverType(@NotNull KotlinType type) {
this.captureReceiverType = type;
}
@NotNull
@Override
public List<Pair<String, Type>> getRecordedFields() {
@@ -490,16 +490,26 @@ public class InlineCodegen extends CallGenerator {
: state.getTypeMapper().mapImplementationOwner(descriptor).getInternalName()
);
FunctionGenerationStrategy strategy =
expression instanceof KtCallableReferenceExpression ?
new FunctionReferenceGenerationStrategy(
state,
descriptor,
CallUtilKt.getResolvedCallWithAssert(
((KtCallableReferenceExpression) expression).getCallableReference(), codegen.getBindingContext()
)
) :
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, (KtDeclarationWithBody) expression);
FunctionGenerationStrategy strategy;
if (expression instanceof KtCallableReferenceExpression) {
KtCallableReferenceExpression callableReferenceExpression = (KtCallableReferenceExpression) expression;
KtExpression receiverExpression = callableReferenceExpression.getReceiverExpression();
StackValue receiverValue =
receiverExpression != null && codegen.getBindingContext().getType(receiverExpression) != null
? codegen.gen(receiverExpression)
: null;
strategy = new FunctionReferenceGenerationStrategy(
state,
descriptor,
CallUtilKt.getResolvedCallWithAssert(callableReferenceExpression.getCallableReference(), codegen.getBindingContext()),
receiverValue != null ? receiverValue.type : null,
receiverValue
);
}
else {
strategy = new FunctionGenerationStrategy.FunctionDefault(state, descriptor, (KtDeclarationWithBody) expression);
}
FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen);
@@ -735,7 +745,7 @@ public class InlineCodegen extends CallGenerator {
private void putClosureParametersOnStack() {
for (LambdaInfo next : expressionMap.values()) {
activeLambda = next;
codegen.pushClosureOnStack(next.getClassDescriptor(), true, this);
codegen.pushClosureOnStack(next.getClassDescriptor(), true, this, /* functionReferenceReceiver = */ null);
}
activeLambda = null;
}
@@ -0,0 +1,5 @@
inline fun foo(x: () -> String) = x()
fun String.id() = this
fun box() = foo("OK"::id)
@@ -0,0 +1,4 @@
fun box(): String {
val f = "KOTLIN"::get
return "${f(1)}${f(0)}"
}
@@ -0,0 +1,5 @@
fun box(): String {
val f = "kotlin"::length
val result = f.get()
return if (result == 6) "OK" else "Fail: $result"
}
@@ -1438,6 +1438,42 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("compiler/testData/codegen/box/callableReference/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/callableReference/bound"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("simpleFunction.kt")
public void testSimpleFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/simpleFunction.kt");
doTest(fileName);
}
@TestMetadata("simpleProperty.kt")
public void testSimpleProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/simpleProperty.kt");
doTest(fileName);
}
@TestMetadata("compiler/testData/codegen/box/callableReference/bound/inline")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Inline extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInInline() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/callableReference/bound/inline"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/bound/inline/simple.kt");
doTest(fileName);
}
}
}
@TestMetadata("compiler/testData/codegen/box/callableReference/function")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)