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.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();
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
+7
-14
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user