/*
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.annotations.
Nullable;
import org.jetbrains.kotlin.codegen.intrinsics.
JavaClassProperty;
import org.jetbrains.kotlin.codegen.state.
GenerationState;
import org.jetbrains.kotlin.codegen.state.
KotlinTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.resolve.
InlineClassesUtilsKt;
import org.jetbrains.kotlin.resolve.calls.model.
ResolvedCall;
import org.jetbrains.kotlin.types.
KotlinType;
import org.jetbrains.org.objectweb.asm.
Type;
import org.jetbrains.org.objectweb.asm.commons.
InstructionAdapter;
public class
CallReceiver extends
StackValue {
private final
StackValue dispatchReceiver;
private final
StackValue extensionReceiver;
private final
Type secondReceiverType;
private final
KotlinType secondReceiverKotlinType;
private
CallReceiver(
@
NotNull StackValue dispatchReceiver,
@
NotNull StackValue extensionReceiver,
@
NotNull Type type,
@
Nullable KotlinType kotlinType,
@
Nullable Type secondReceiverType,
@
Nullable KotlinType secondReceiverKotlinType
) {
super(
type,
kotlinType,
dispatchReceiver.
canHaveSideEffects() ||
extensionReceiver.
canHaveSideEffects());
this.
dispatchReceiver =
dispatchReceiver;
this.
extensionReceiver =
extensionReceiver;
this.
secondReceiverType =
secondReceiverType;
this.
secondReceiverKotlinType =
secondReceiverKotlinType;
}
public
StackValue withoutReceiverArgument() {
return new
CallReceiver(
dispatchReceiver,
none(),
type,
kotlinType,
secondReceiverType,
secondReceiverKotlinType);
}
public static
StackValue generateCallReceiver(
@
NotNull ResolvedCall<?>
resolvedCall,
@
NotNull ExpressionCodegen codegen,
@
Nullable Callable callableMethod,
@
Nullable ReceiverParameterDescriptor dispatchReceiverParameter,
@
NotNull StackValue dispatchReceiver,
@
Nullable ReceiverParameterDescriptor extensionReceiverParameter,
@
NotNull StackValue extensionReceiver
) {
KotlinTypeMapper typeMapper =
codegen.
typeMapper;
GenerationState state =
codegen.
getState();
JvmKotlinType jvmKotlinType;
Type secondReceiverType = null;
KotlinType secondReceiverKotlinType = null;
if (
extensionReceiverParameter != null) {
jvmKotlinType =
calcExtensionReceiverType(
resolvedCall,
extensionReceiverParameter,
typeMapper,
callableMethod,
state
);
if (
dispatchReceiverParameter != null) {
JvmKotlinType dispatchReceiverInfo =
calcDispatchReceiverType(
resolvedCall,
dispatchReceiverParameter,
typeMapper,
callableMethod
);
secondReceiverType =
dispatchReceiverInfo.
getType();
secondReceiverKotlinType =
dispatchReceiverInfo.
getKotlinType();
}
}
else if (
dispatchReceiverParameter != null) {
jvmKotlinType =
calcDispatchReceiverType(
resolvedCall,
dispatchReceiverParameter,
typeMapper,
callableMethod);
}
else if (
isLocalFunCall(
callableMethod)) {
Type calleeType =
callableMethod.
getGenerateCalleeType();
assert
calleeType != null : "Could not get callee type for " +
resolvedCall;
jvmKotlinType = new
JvmKotlinType(
calleeType, null);
}
else {
jvmKotlinType = new
JvmKotlinType(
Type.
VOID_TYPE, null);
}
return new
CallReceiver(
dispatchReceiver,
extensionReceiver,
jvmKotlinType.
getType(),
jvmKotlinType.
getKotlinType(),
secondReceiverType,
secondReceiverKotlinType
);
}
private static
JvmKotlinType calcDispatchReceiverType(
@
NotNull ResolvedCall<?>
resolvedCall,
@
Nullable ReceiverParameterDescriptor dispatchReceiver,
@
NotNull KotlinTypeMapper typeMapper,
@
Nullable Callable callableMethod
) {
if (
dispatchReceiver == null) return null;
CallableDescriptor descriptor =
resolvedCall.
getResultingDescriptor();
if (
CodegenUtilKt.
isJvmStaticInObjectOrClassOrInterface(
descriptor)) {
return new
JvmKotlinType(
Type.
VOID_TYPE, null);
}
DeclarationDescriptor container =
descriptor.
getContainingDeclaration();
if (
callableMethod != null) {
if (
InlineClassesUtilsKt.
isInlineClass(
container)) {
ClassDescriptor classDescriptor = (
ClassDescriptor)
container;
return new
JvmKotlinType(
typeMapper.
mapType(
classDescriptor),
classDescriptor.
getDefaultType());
}
//noinspection ConstantConditions
return new
JvmKotlinType(
callableMethod.
getDispatchReceiverType(),
callableMethod.
getDispatchReceiverKotlinType());
}
// Extract the receiver from the resolved call, workarounding the fact that ResolvedCall#dispatchReceiver doesn't have
// all the needed information, for example there's no way to find out whether or not a smart cast was applied to the receiver.
if (
container instanceof
ClassDescriptor) {
ClassDescriptor classDescriptor = (
ClassDescriptor)
container;
return new
JvmKotlinType(
typeMapper.
mapType(
classDescriptor),
classDescriptor.
getDefaultType());
}
KotlinType dispatchReceiverType =
dispatchReceiver.
getReturnType();
//noinspection ConstantConditions
return new
JvmKotlinType(
typeMapper.
mapType(
dispatchReceiverType),
dispatchReceiverType);
}
private static
JvmKotlinType calcExtensionReceiverType(
@
NotNull ResolvedCall<?>
resolvedCall,
@
Nullable ReceiverParameterDescriptor extensionReceiver,
@
NotNull KotlinTypeMapper typeMapper,
@
Nullable Callable callableMethod,
@
NotNull GenerationState state
) {
if (
extensionReceiver == null) return null;
CallableDescriptor descriptor =
resolvedCall.
getCandidateDescriptor();
if (
descriptor instanceof
PropertyDescriptor &&
// hackaround: boxing changes behaviour of T.javaClass intrinsic
state.
getIntrinsics().
getIntrinsic((
PropertyDescriptor)
descriptor) !=
JavaClassProperty.
INSTANCE
) {
ReceiverParameterDescriptor receiverCandidate =
descriptor.
getExtensionReceiverParameter();
assert
receiverCandidate != null;
return new
JvmKotlinType(
typeMapper.
mapType(
receiverCandidate.
getType()),
receiverCandidate.
getType());
}
return
callableMethod != null ?
new
JvmKotlinType(
callableMethod.
getExtensionReceiverType(),
callableMethod.
getExtensionReceiverKotlinType()) :
new
JvmKotlinType(
typeMapper.
mapType(
extensionReceiver.
getType()),
extensionReceiver.
getType());
}
@
Override
public void
putSelector(@
NotNull Type type, @
Nullable KotlinType kotlinType, @
NotNull InstructionAdapter v) {
StackValue currentExtensionReceiver =
extensionReceiver;
boolean
hasExtensionReceiver =
extensionReceiver !=
none();
if (
extensionReceiver instanceof
SafeCall) {
currentExtensionReceiver.
put(
currentExtensionReceiver.
type,
currentExtensionReceiver.
kotlinType,
v);
currentExtensionReceiver =
StackValue.
onStack(
currentExtensionReceiver.
type,
currentExtensionReceiver.
kotlinType);
}
Type dispatchReceiverType =
calcDispatchReceiver(
secondReceiverType,
hasExtensionReceiver,
dispatchReceiver.
type,
type);
KotlinType dispatchReceiverKotlinType =
calcDispatchReceiver(
secondReceiverKotlinType,
hasExtensionReceiver,
dispatchReceiver.
kotlinType,
kotlinType
);
dispatchReceiver.
put(
dispatchReceiverType,
dispatchReceiverKotlinType,
v);
currentExtensionReceiver
.
moveToTopOfStack(
hasExtensionReceiver ?
type :
currentExtensionReceiver.
type,
hasExtensionReceiver ?
kotlinType :
currentExtensionReceiver.
kotlinType,
v,
dispatchReceiverType.
getSize()
);
}
private static <T> T
calcDispatchReceiver(T
secondType, boolean
hasExtensionReceiver, T
dispatchReceiverType, T
defaultType) {
if (
secondType != null) {
return
secondType;
}
else {
return
hasExtensionReceiver ?
dispatchReceiverType :
defaultType;
}
}
@
Override
public void
dup(@
NotNull InstructionAdapter v, boolean
withReceiver) {
AsmUtil.
dup(
v,
extensionReceiver.
type,
dispatchReceiver.
type);
}
@
NotNull
public
StackValue getDispatchReceiver() {
return
dispatchReceiver;
}
@
NotNull
public
StackValue getExtensionReceiver() {
return
extensionReceiver;
}
}