Fix toString() and reflectLambda() for lambdas with generic types in signatures
#KT-10771 Fixed
This commit is contained in:
@@ -63,6 +63,7 @@ import static org.jetbrains.kotlin.codegen.ExpressionCodegen.generateClassLitera
|
|||||||
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConst;
|
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConst;
|
||||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE;
|
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE;
|
||||||
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
|
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.asmTypeForAnonymousClass;
|
||||||
|
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
|
||||||
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
|
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
|
||||||
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
|
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
|
||||||
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
|
||||||
@@ -228,10 +229,15 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void generateKotlinMetadataAnnotation() {
|
protected void generateKotlinMetadataAnnotation() {
|
||||||
|
FunctionDescriptor freeLambdaDescriptor = createFreeLambdaDescriptor(funDescriptor);
|
||||||
|
Method method = v.getSerializationBindings().get(METHOD_FOR_FUNCTION, funDescriptor);
|
||||||
|
assert method != null : "No method for " + funDescriptor;
|
||||||
|
v.getSerializationBindings().put(METHOD_FOR_FUNCTION, freeLambdaDescriptor, method);
|
||||||
|
|
||||||
final DescriptorSerializer serializer =
|
final DescriptorSerializer serializer =
|
||||||
DescriptorSerializer.createForLambda(new JvmSerializerExtension(v.getSerializationBindings(), state));
|
DescriptorSerializer.createForLambda(new JvmSerializerExtension(v.getSerializationBindings(), state));
|
||||||
|
|
||||||
final ProtoBuf.Function functionProto = serializer.functionProto(funDescriptor).build();
|
final ProtoBuf.Function functionProto = serializer.functionProto(freeLambdaDescriptor).build();
|
||||||
|
|
||||||
WriteAnnotationUtilKt.writeKotlinMetadata(v, KotlinClassHeader.Kind.SYNTHETIC_CLASS, new Function1<AnnotationVisitor, Unit>() {
|
WriteAnnotationUtilKt.writeKotlinMetadata(v, KotlinClassHeader.Kind.SYNTHETIC_CLASS, new Function1<AnnotationVisitor, Unit>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -242,6 +248,30 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a function descriptor, creates another function descriptor with type parameters copied from outer context(s).
|
||||||
|
* This is needed because once we're serializing this to a proto, there's no place to store information about external type parameters.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private static FunctionDescriptor createFreeLambdaDescriptor(@NotNull FunctionDescriptor descriptor) {
|
||||||
|
FunctionDescriptor.CopyBuilder<? extends FunctionDescriptor> builder = descriptor.newCopyBuilder();
|
||||||
|
List<TypeParameterDescriptor> typeParameters = new ArrayList<TypeParameterDescriptor>(0);
|
||||||
|
builder.setTypeParameters(typeParameters);
|
||||||
|
|
||||||
|
DeclarationDescriptor container = descriptor.getContainingDeclaration();
|
||||||
|
while (container != null) {
|
||||||
|
if (container instanceof ClassDescriptor) {
|
||||||
|
typeParameters.addAll(((ClassDescriptor) container).getDeclaredTypeParameters());
|
||||||
|
}
|
||||||
|
else if (container instanceof CallableDescriptor && !(container instanceof ConstructorDescriptor)) {
|
||||||
|
typeParameters.addAll(((CallableDescriptor) container).getTypeParameters());
|
||||||
|
}
|
||||||
|
container = container.getContainingDeclaration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeParameters.isEmpty() ? descriptor : builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void done() {
|
protected void done() {
|
||||||
writeOuterClassAndEnclosingMethod();
|
writeOuterClassAndEnclosingMethod();
|
||||||
|
|||||||
+5
@@ -273,6 +273,11 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes
|
|||||||
LazyClassDescriptor.this, null, Annotations.Companion.getEMPTY(), Name.special("<init-blocks>"),
|
LazyClassDescriptor.this, null, Annotations.Companion.getEMPTY(), Name.special("<init-blocks>"),
|
||||||
CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE
|
CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE
|
||||||
) {
|
) {
|
||||||
|
{
|
||||||
|
initialize(null, null, Collections.<TypeParameterDescriptor>emptyList(), Collections.<ValueParameterDescriptor>emptyList(),
|
||||||
|
null, Modality.FINAL, Visibilities.PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
protected FunctionDescriptorImpl createSubstitutedCopy(
|
protected FunctionDescriptorImpl createSubstitutedCopy(
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// WITH_REFLECT
|
||||||
|
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
fun <T> bar(): String {
|
||||||
|
return { t: T -> t }.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Baz<T, V> {
|
||||||
|
fun <V : T> baz(v: V): String {
|
||||||
|
return (fun(t: List<T>): V = v).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Foo<T, U : List<T>>(val lambda: (T) -> U)
|
||||||
|
class Bar<T> : Foo<T, List<T>>({ listOf(it) })
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals("(T) -> T", bar<String>())
|
||||||
|
assertEquals("(kotlin.collections.List<T>) -> V", Baz<String, Int>().baz<String>(""))
|
||||||
|
assertEquals("(T) -> kotlin.collections.List<T>", Bar<Int>().lambda.toString())
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -6469,6 +6469,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionNtoStringGeneric.kt")
|
||||||
|
public void testFunctionNtoStringGeneric() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionNtoStringGeneric.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("functionNtoStringNoReflect.kt")
|
@TestMetadata("functionNtoStringNoReflect.kt")
|
||||||
public void testFunctionNtoStringNoReflect() throws Exception {
|
public void testFunctionNtoStringNoReflect() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionNtoStringNoReflect.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionNtoStringNoReflect.kt");
|
||||||
|
|||||||
+7
-14
@@ -563,21 +563,14 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
configuration.newOwner, configuration.original, configuration.kind, configuration.name, resultAnnotations,
|
configuration.newOwner, configuration.original, configuration.kind, configuration.name, resultAnnotations,
|
||||||
getSourceToUseForCopy(configuration.preserveSourceElement, configuration.original, configuration.sourceElement));
|
getSourceToUseForCopy(configuration.preserveSourceElement, configuration.original, configuration.sourceElement));
|
||||||
|
|
||||||
List<TypeParameterDescriptor> substitutedTypeParameters;
|
List<TypeParameterDescriptor> unsubstitutedTypeParameters =
|
||||||
final TypeSubstitutor substitutor;
|
configuration.newTypeParameters == null ? getTypeParameters() : configuration.newTypeParameters;
|
||||||
|
|
||||||
if (configuration.newTypeParameters == null) {
|
List<TypeParameterDescriptor> substitutedTypeParameters =
|
||||||
List<TypeParameterDescriptor> originalTypeParameters = getTypeParameters();
|
new ArrayList<TypeParameterDescriptor>(unsubstitutedTypeParameters.size());
|
||||||
substitutedTypeParameters = new ArrayList<TypeParameterDescriptor>(originalTypeParameters.size());
|
final TypeSubstitutor substitutor = DescriptorSubstitutor.substituteTypeParameters(
|
||||||
substitutor = DescriptorSubstitutor.substituteTypeParameters(
|
unsubstitutedTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters
|
||||||
originalTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// They should be already substituted
|
|
||||||
substitutedTypeParameters = configuration.newTypeParameters;
|
|
||||||
substitutor = configuration.substitution.buildSubstitutor();
|
|
||||||
}
|
|
||||||
|
|
||||||
KotlinType substitutedReceiverParameterType = null;
|
KotlinType substitutedReceiverParameterType = null;
|
||||||
if (configuration.newExtensionReceiverParameterType != null) {
|
if (configuration.newExtensionReceiverParameterType != null) {
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ fun <R> Function<R>.reflect(): KFunction<R>? {
|
|||||||
val proto = ProtoBuf.Function.parseFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY)
|
val proto = ProtoBuf.Function.parseFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY)
|
||||||
val moduleData = javaClass.getOrCreateModule()
|
val moduleData = javaClass.getOrCreateModule()
|
||||||
val context = DeserializationContext(
|
val context = DeserializationContext(
|
||||||
moduleData.deserialization, nameResolver, moduleData.module,
|
moduleData.deserialization, nameResolver, moduleData.module, TypeTable(proto.typeTable),
|
||||||
typeTable = TypeTable(proto.typeTable), containerSource = null, parentTypeDeserializer = null, typeParameters = listOf()
|
containerSource = null, parentTypeDeserializer = null, typeParameters = proto.typeParameterList
|
||||||
)
|
)
|
||||||
val descriptor = MemberDeserializer(context).loadFunction(proto)
|
val descriptor = MemberDeserializer(context).loadFunction(proto)
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
|||||||
Reference in New Issue
Block a user