Fix toString() and reflectLambda() for lambdas with generic types in signatures

#KT-10771 Fixed
This commit is contained in:
Alexander Udalov
2016-06-08 19:32:11 +03:00
parent dc2972d99e
commit 59342000ae
6 changed files with 74 additions and 17 deletions
@@ -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.binding.CodegenBinding.CLOSURE;
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.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
import static org.jetbrains.org.objectweb.asm.Opcodes.*;
@@ -228,10 +229,15 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
@Override
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 =
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>() {
@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
protected void done() {
writeOuterClassAndEnclosingMethod();
@@ -273,6 +273,11 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes
LazyClassDescriptor.this, null, Annotations.Companion.getEMPTY(), Name.special("<init-blocks>"),
CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE
) {
{
initialize(null, null, Collections.<TypeParameterDescriptor>emptyList(), Collections.<ValueParameterDescriptor>emptyList(),
null, Modality.FINAL, Visibilities.PRIVATE);
}
@NotNull
@Override
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);
}
@TestMetadata("functionNtoStringGeneric.kt")
public void testFunctionNtoStringGeneric() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionNtoStringGeneric.kt");
doTest(fileName);
}
@TestMetadata("functionNtoStringNoReflect.kt")
public void testFunctionNtoStringNoReflect() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionNtoStringNoReflect.kt");
@@ -563,21 +563,14 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
configuration.newOwner, configuration.original, configuration.kind, configuration.name, resultAnnotations,
getSourceToUseForCopy(configuration.preserveSourceElement, configuration.original, configuration.sourceElement));
List<TypeParameterDescriptor> substitutedTypeParameters;
final TypeSubstitutor substitutor;
List<TypeParameterDescriptor> unsubstitutedTypeParameters =
configuration.newTypeParameters == null ? getTypeParameters() : configuration.newTypeParameters;
if (configuration.newTypeParameters == null) {
List<TypeParameterDescriptor> originalTypeParameters = getTypeParameters();
substitutedTypeParameters = new ArrayList<TypeParameterDescriptor>(originalTypeParameters.size());
substitutor = DescriptorSubstitutor.substituteTypeParameters(
originalTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters
);
}
else {
// They should be already substituted
substitutedTypeParameters = configuration.newTypeParameters;
substitutor = configuration.substitution.buildSubstitutor();
}
List<TypeParameterDescriptor> substitutedTypeParameters =
new ArrayList<TypeParameterDescriptor>(unsubstitutedTypeParameters.size());
final TypeSubstitutor substitutor = DescriptorSubstitutor.substituteTypeParameters(
unsubstitutedTypeParameters, configuration.substitution, substitutedDescriptor, substitutedTypeParameters
);
KotlinType substitutedReceiverParameterType = 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 moduleData = javaClass.getOrCreateModule()
val context = DeserializationContext(
moduleData.deserialization, nameResolver, moduleData.module,
typeTable = TypeTable(proto.typeTable), containerSource = null, parentTypeDeserializer = null, typeParameters = listOf()
moduleData.deserialization, nameResolver, moduleData.module, TypeTable(proto.typeTable),
containerSource = null, parentTypeDeserializer = null, typeParameters = proto.typeParameterList
)
val descriptor = MemberDeserializer(context).loadFunction(proto)
@Suppress("UNCHECKED_CAST")