Support calling methods annotated with PolymorphicSignature

#KT-14416 Fixed
 #KT-26165 Fixed
This commit is contained in:
Alexander Udalov
2018-11-12 17:38:42 +01:00
parent b940418604
commit a796f11934
18 changed files with 595 additions and 25 deletions
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
@@ -22,8 +24,14 @@ class CallBasedArgumentGenerator(
private val isVarargInvoke: Boolean =
JvmCodegenUtil.isDeclarationOfBigArityFunctionInvoke(valueParameters.firstOrNull()?.containingDeclaration)
private val isPolymorphicSignature: Boolean =
codegen.state.languageVersionSettings.supportsFeature(LanguageFeature.PolymorphicSignature) &&
(valueParameters.firstOrNull()?.containingDeclaration as? FunctionDescriptor)?.let { function ->
JvmCodegenUtil.isPolymorphicSignature(function)
} == true
init {
if (!isVarargInvoke) {
if (!isVarargInvoke && !isPolymorphicSignature) {
assert(valueParameters.size == valueParameterTypes.size) {
"Value parameters and their types mismatch in sizes: ${valueParameters.size} != ${valueParameterTypes.size}"
}
@@ -49,6 +57,15 @@ class CallBasedArgumentGenerator(
}
override fun generateVararg(i: Int, argument: VarargValueArgument) {
if (isPolymorphicSignature) {
for ((index, arg) in argument.arguments.withIndex()) {
val expression = arg.getArgumentExpression()!!
val type = JvmKotlinType(valueParameterTypes[index], codegen.kotlinType(expression))
callGenerator.genValueAndPut(null, expression, type, index)
}
return
}
// Upper bound for type of vararg parameter should always have a form of 'Array<out T>',
// while its lower bound may be Nothing-typed after approximation
val lazyVararg = codegen.genVarargs(argument, valueParameters[i].type.upperIfFlexible())
@@ -2401,12 +2401,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return intrinsic.toCallable(fd, superCall, resolvedCall, this);
}
return resolveToCallableMethod(fd, superCall);
}
@NotNull
private CallableMethod resolveToCallableMethod(@NotNull FunctionDescriptor fd, boolean superCall) {
return typeMapper.mapToCallableMethod(SamCodegenUtil.resolveSamAdapter(fd), superCall);
return typeMapper.mapToCallableMethod(SamCodegenUtil.resolveSamAdapter(fd), resolvedCall, superCall, null);
}
public void invokeMethodWithArguments(
@@ -4381,7 +4376,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
ResolvedCall<FunctionDescriptor> resolvedCall = isGetter ? resolvedGetCall : resolvedSetCall;
assert resolvedCall != null : "No resolved call for " + operationDescriptor;
Callable callable = resolveToCallable(accessibleFunctionDescriptor(resolvedCall), false, resolvedCall);
Callable callableMethod = resolveToCallableMethod(operationDescriptor, false);
Callable callableMethod = typeMapper.mapToCallableMethod(SamCodegenUtil.resolveSamAdapter(operationDescriptor), false);
Type[] argumentTypes = callableMethod.getParameterTypes();
StackValue.CollectionElementReceiver collectionElementReceiver = createCollectionElementReceiver(
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor;
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor;
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt;
import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.Call;
import org.jetbrains.kotlin.psi.KtFile;
@@ -384,4 +385,8 @@ public class JvmCodegenUtil {
: "ClassDescriptor should not be null:" + specifier.getText();
return superClass;
}
public static boolean isPolymorphicSignature(@NotNull FunctionDescriptor descriptor) {
return descriptor.getAnnotations().hasAnnotation(new FqName("java.lang.invoke.MethodHandle.PolymorphicSignature"));
}
}
@@ -46,14 +46,13 @@ import org.jetbrains.kotlin.load.kotlin.*;
import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider.IncrementalMultifileClassPackageFragment;
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmBytecodeBinaryVersion;
import org.jetbrains.kotlin.name.*;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtFunctionLiteral;
import org.jetbrains.kotlin.psi.KtLambdaExpression;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
import org.jetbrains.kotlin.resolve.*;
import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.kotlin.resolve.calls.model.VarargValueArgument;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature;
@@ -791,7 +790,12 @@ public class KotlinTypeMapper {
}
@NotNull
public CallableMethod mapToCallableMethod(@NotNull FunctionDescriptor descriptor, boolean superCall, @Nullable OwnerKind kind) {
public CallableMethod mapToCallableMethod(
@NotNull FunctionDescriptor descriptor,
@Nullable ResolvedCall<?> resolvedCall,
boolean superCall,
@Nullable OwnerKind kind
) {
// we generate constructors of inline classes as usual functions
if (descriptor instanceof ConstructorDescriptor && kind != OwnerKind.ERASED_INLINE_CLASS) {
JvmMethodSignature method = mapSignatureSkipGeneric(descriptor.getOriginal());
@@ -904,7 +908,7 @@ public class KotlinTypeMapper {
signature = toInlinedErasedClass
? mapSignatureForInlineErasedClassSkipGeneric(functionToCall)
: mapSignatureSkipGeneric(functionToCall);
: mapSignature(functionToCall, resolvedCall, OwnerKind.IMPLEMENTATION, true, false);
returnKotlinType = functionToCall.getReturnType();
ClassDescriptor receiver = (currentIsInterface && !originalIsInterface) || currentOwner instanceof FunctionClassDescriptor
@@ -965,6 +969,11 @@ public class KotlinTypeMapper {
);
}
@NotNull
public CallableMethod mapToCallableMethod(@NotNull FunctionDescriptor descriptor, boolean superCall, @Nullable OwnerKind kind) {
return mapToCallableMethod(descriptor, null, superCall, kind);
}
@NotNull
public CallableMethod mapToCallableMethod(@NotNull FunctionDescriptor descriptor, boolean superCall) {
return mapToCallableMethod(descriptor, superCall, null);
@@ -1215,17 +1224,20 @@ public class KotlinTypeMapper {
}
@NotNull
public JvmMethodGenericSignature mapSignatureWithGeneric(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind, boolean hasSpecialBridge) {
return mapSignature(f, kind, false, hasSpecialBridge);
public JvmMethodGenericSignature mapSignatureWithGeneric(
@NotNull FunctionDescriptor f, @NotNull OwnerKind kind, boolean hasSpecialBridge
) {
return mapSignature(f, null, kind, false, hasSpecialBridge);
}
private JvmMethodGenericSignature mapSignature(@NotNull FunctionDescriptor f, @NotNull OwnerKind kind, boolean skipGenericSignature) {
return mapSignature(f, kind, skipGenericSignature, false);
return mapSignature(f, null, kind, skipGenericSignature, false);
}
@NotNull
private JvmMethodGenericSignature mapSignature(
@NotNull FunctionDescriptor f,
@Nullable ResolvedCall<?> resolvedCall,
@NotNull OwnerKind kind,
boolean skipGenericSignature,
boolean hasSpecialBridge
@@ -1254,13 +1266,25 @@ public class KotlinTypeMapper {
if (isDeclarationOfBigArityFunctionInvoke(f) || isDeclarationOfBigArityCreateCoroutineMethod(f)) {
KotlinBuiltIns builtIns = DescriptorUtilsKt.getBuiltIns(f);
KotlinType arrayOfNullableAny = builtIns.getArrayType(Variance.INVARIANT, builtIns.getNullableAnyType());
return mapSignatureWithCustomParameters(f, kind, Stream.of(arrayOfNullableAny), false, false);
return mapSignatureWithCustomParameters(f, kind, Stream.of(arrayOfNullableAny), null, false, false);
}
return mapSignatureWithCustomParameters(
f, kind, f.getValueParameters().stream().map(ValueParameterDescriptor::getType),
skipGenericSignature, hasSpecialBridge
);
Stream<KotlinType> parameterTypes;
KotlinType returnType;
if (languageVersionSettings.supportsFeature(LanguageFeature.PolymorphicSignature) && JvmCodegenUtil.isPolymorphicSignature(f)) {
if (resolvedCall == null) {
throw new UnsupportedOperationException("Cannot determine polymorphic signature without a resolved call: " + f);
}
parameterTypes = extractPolymorphicParameterTypes(resolvedCall);
returnType = extractPolymorphicReturnType(resolvedCall);
}
else {
parameterTypes = f.getValueParameters().stream().map(ValueParameterDescriptor::getType);
returnType = null;
}
return mapSignatureWithCustomParameters(f, kind, parameterTypes, returnType, skipGenericSignature, hasSpecialBridge);
}
@NotNull
@@ -1271,8 +1295,7 @@ public class KotlinTypeMapper {
boolean skipGenericSignature
) {
return mapSignatureWithCustomParameters(
f, kind, valueParameters.stream().map(ValueParameterDescriptor::getType),
skipGenericSignature, false
f, kind, valueParameters.stream().map(ValueParameterDescriptor::getType), null, skipGenericSignature, false
);
}
@@ -1281,6 +1304,7 @@ public class KotlinTypeMapper {
@NotNull FunctionDescriptor f,
@NotNull OwnerKind kind,
@NotNull Stream<KotlinType> valueParameterTypes,
@Nullable KotlinType customReturnType,
boolean skipGenericSignature,
boolean hasSpecialBridge
) {
@@ -1344,7 +1368,12 @@ public class KotlinTypeMapper {
});
sw.writeReturnType();
mapReturnType(f, sw);
if (customReturnType != null) {
mapReturnType(f, sw, customReturnType);
}
else {
mapReturnType(f, sw);
}
sw.writeReturnTypeEnd();
}
@@ -1363,6 +1392,54 @@ public class KotlinTypeMapper {
return signature;
}
@NotNull
private Stream<KotlinType> extractPolymorphicParameterTypes(@NotNull ResolvedCall<?> resolvedCall) {
List<ResolvedValueArgument> valueArgumentsByIndex = resolvedCall.getValueArgumentsByIndex();
if (valueArgumentsByIndex == null ||
valueArgumentsByIndex.size() != 1 ||
!(valueArgumentsByIndex.get(0) instanceof VarargValueArgument)) {
throw new UnsupportedOperationException(
"Polymorphic signature is only supported for methods with one vararg argument: " + resolvedCall.getResultingDescriptor()
);
}
VarargValueArgument argument = (VarargValueArgument) valueArgumentsByIndex.get(0);
return argument.getArguments().stream().map(arg -> {
KtExpression expression = arg.getArgumentExpression();
if (expression == null) {
throw new UnsupportedOperationException(
"Polymorphic signature argument must be an expression: " + resolvedCall.getResultingDescriptor()
);
}
return bindingContext.getType(expression);
});
}
@Nullable
private KotlinType extractPolymorphicReturnType(@NotNull ResolvedCall<?> resolvedCall) {
// Return type is polymorphic only in case it's Object; see VarHandle.compareAndSet and similar.
KotlinType originalReturnType = resolvedCall.getResultingDescriptor().getReturnType();
if (originalReturnType != null && !KotlinBuiltIns.isAny(originalReturnType)) return null;
if (!(resolvedCall.getCall().getCallElement() instanceof KtExpression)) {
throw new UnsupportedOperationException(
"Polymorphic signature method call must be an expression: " + resolvedCall.getResultingDescriptor()
);
}
KtExpression expression = (KtExpression) resolvedCall.getCall().getCallElement();
while (true) {
KtQualifiedExpression parent = KtPsiUtilKt.getQualifiedExpressionForSelector(expression);
if (parent == null) break;
expression = parent;
}
expression = KtPsiUtilKt.getOutermostParenthesizerOrThis(expression);
return expression.getParent() instanceof KtBinaryExpressionWithTypeRHS
? bindingContext.getType((KtExpression) expression.getParent())
: null;
}
private void checkOwnerCompatibility(@NotNull FunctionDescriptor descriptor) {
KotlinJvmBinaryClass ownerClass = KotlinJvmBinaryClassUtilKt.getContainingKotlinJvmBinaryClass(descriptor);
if (ownerClass == null) return;