From 534a3a11d6144fda38228a56b70bcbd1a8e63420 Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Mon, 29 Feb 2016 14:39:49 +0300 Subject: [PATCH] Write additional type parameters for DefaultImpls methods, fix for KT-11121: BadClassFile exception for interface implemented generic properties #KT-11121 Fixed --- .../codegen/state/KotlinTypeMapper.java | 45 ++++------- .../kotlin/codegen/state/typeMapperUtils.kt | 81 +++++++++++++++++++ .../defaultImplsGenericSignature.kt | 44 ++++++++++ .../reflection/genericSignature/kt11121.kt | 23 ++++++ .../functionTypeParameterClash.kt | 13 +++ .../functionTypeParameterClashWith_I.kt | 13 +++ .../propertyTypeParameterClash.kt | 11 +++ .../BlackBoxWithJavaCodegenTestGenerated.java | 6 ++ ...lackBoxWithStdlibCodegenTestGenerated.java | 6 ++ .../compiler/WriteSignatureTestGenerated.java | 27 +++++++ 10 files changed, 240 insertions(+), 29 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/state/typeMapperUtils.kt create mode 100644 compiler/testData/codegen/boxWithJava/reflection/defaultImplsGenericSignature.kt create mode 100644 compiler/testData/codegen/boxWithStdlib/reflection/genericSignature/kt11121.kt create mode 100644 compiler/testData/writeSignature/defaultImpls/functionTypeParameterClash.kt create mode 100644 compiler/testData/writeSignature/defaultImpls/functionTypeParameterClashWith_I.kt create mode 100644 compiler/testData/writeSignature/defaultImpls/propertyTypeParameterClash.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index cf9f1ed4685..706ea9ac8bb 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -1059,10 +1059,24 @@ public class KotlinTypeMapper { writeVoidReturn(sw); } else { - writeFormalTypeParameters(getDirectMember(f).getTypeParameters(), sw); + CallableMemberDescriptor directMember = getDirectMember(f); + KotlinType thisIfNeeded = null; + if (OwnerKind.DEFAULT_IMPLS == kind) { + ReceiverTypeAndTypeParameters receiverTypeAndTypeParameters = TypeMapperUtilsKt.patchTypeParametersForDefaultImplMethod(directMember); + writeFormalTypeParameters(CollectionsKt.plus(receiverTypeAndTypeParameters.getTypeParameters(), directMember.getTypeParameters()), sw); + thisIfNeeded = receiverTypeAndTypeParameters.getReceiverType(); + } + else { + writeFormalTypeParameters(directMember.getTypeParameters(), sw); + if (isAccessor(f) && f.getDispatchReceiverParameter() != null) { + thisIfNeeded = ((ClassDescriptor) f.getContainingDeclaration()).getDefaultType(); + } + } sw.writeParametersStart(); - writeThisIfNeeded(f, kind, sw); + if (thisIfNeeded != null) { + writeParameter(sw, JvmMethodParameterKind.THIS, thisIfNeeded, f); + } ReceiverParameterDescriptor receiverParameter = f.getExtensionReceiverParameter(); if (receiverParameter != null) { @@ -1216,33 +1230,6 @@ public class KotlinTypeMapper { return sw.makeJavaGenericSignature(); } - private void writeThisIfNeeded( - @NotNull CallableMemberDescriptor descriptor, - @NotNull OwnerKind kind, - @NotNull JvmSignatureWriter sw - ) { - ClassDescriptor thisType; - if (kind == OwnerKind.DEFAULT_IMPLS) { - thisType = getTraitImplThisParameterClass((ClassDescriptor) descriptor.getContainingDeclaration()); - } - else if (isAccessor(descriptor) && descriptor.getDispatchReceiverParameter() != null) { - thisType = (ClassDescriptor) descriptor.getContainingDeclaration(); - } - else return; - - writeParameter(sw, JvmMethodParameterKind.THIS, thisType.getDefaultType(), descriptor); - } - - @NotNull - private static ClassDescriptor getTraitImplThisParameterClass(@NotNull ClassDescriptor traitDescriptor) { - for (ClassDescriptor descriptor : DescriptorUtils.getSuperclassDescriptors(traitDescriptor)) { - if (descriptor.getKind() != ClassKind.INTERFACE) { - return descriptor; - } - } - return traitDescriptor; - } - public void writeFormalTypeParameters(@NotNull List typeParameters, @NotNull JvmSignatureWriter sw) { if (sw.skipGenericSignature()) return; for (TypeParameterDescriptor typeParameter : typeParameters) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/typeMapperUtils.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/typeMapperUtils.kt new file mode 100644 index 00000000000..8437273baf9 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/typeMapperUtils.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.state + +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection + +class ReceiverTypeAndTypeParameters(val receiverType: KotlinType, val typeParameters: List) + +fun patchTypeParametersForDefaultImplMethod(function: CallableMemberDescriptor): ReceiverTypeAndTypeParameters { + val classDescriptor = function.containingDeclaration as ClassDescriptor + val functionTypeParameterNames = function.typeParameters.map { it.name.asString() } + val interfaceTypeParameters = classDescriptor.declaredTypeParameters + val conflictedTypeParameters = interfaceTypeParameters.filter { it.name.asString() in functionTypeParameterNames } + + if (conflictedTypeParameters.isEmpty()) + return ReceiverTypeAndTypeParameters(classDescriptor.defaultType, interfaceTypeParameters) + + val existingNames = (functionTypeParameterNames + interfaceTypeParameters.map { it.name.asString() }).toMutableSet() + + val mappingForInterfaceTypeParameters = conflictedTypeParameters.associateBy ({ it }) { + typeParameter -> + + val newNamePrefix = typeParameter.name.asString() + "_I" + val newName = newNamePrefix + generateSequence(1) { x -> x + 1 }.first { + index -> (newNamePrefix + index) !in existingNames + } + + existingNames.add(newName) + function.createTypeParameterWithNewName(typeParameter, newName) + } + + val substitution = TypeConstructorSubstitution.createByParametersMap(mappingForInterfaceTypeParameters.mapValues { + it.value.defaultType.asTypeProjection() + }) + + val substitutor = TypeSubstitutor.create(substitution) + + val additionalTypeParameters = interfaceTypeParameters.map { typeParameter -> + mappingForInterfaceTypeParameters[typeParameter] ?: typeParameter + } + var resultTypeParameters = mutableListOf() + DescriptorSubstitutor.substituteTypeParameters(additionalTypeParameters, substitution, classDescriptor, resultTypeParameters) + + return ReceiverTypeAndTypeParameters(substitutor.substitute(classDescriptor.defaultType, Variance.INVARIANT)!!, resultTypeParameters) +} + +fun CallableMemberDescriptor.createTypeParameterWithNewName(descriptor: TypeParameterDescriptor, newName: String): TypeParameterDescriptorImpl { + val newDescriptor = TypeParameterDescriptorImpl.createForFurtherModification( + this, + descriptor.annotations, + descriptor.isReified, + descriptor.variance, + Name.identifier(newName), + descriptor.index, + descriptor.source) + descriptor.upperBounds.forEach { + newDescriptor.addUpperBound(it) + } + newDescriptor.setInitialized() + return newDescriptor +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithJava/reflection/defaultImplsGenericSignature.kt b/compiler/testData/codegen/boxWithJava/reflection/defaultImplsGenericSignature.kt new file mode 100644 index 00000000000..b8e2bbdcfe3 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/reflection/defaultImplsGenericSignature.kt @@ -0,0 +1,44 @@ +// FILE: J.java + +public class J { + + public static int test1() { + A> x = new X>("O", new B("K")); + return A.DefaultImpls.test1(x, 1, 1.0); + } + + + public static A> test2(){ + X> x = new X>("O", new B("K")); + return A.DefaultImpls.test2(x, 1); + } +} + +// FILE: K.kt + +class B(val value: T) + +interface A> { + + fun test1(p: T, z: L): T { + return p + } + + fun test2(p: L): A { + return this + } +} + + +class X>(val p1: T, val p2: Y) : A { + +} + +fun box(): String { + val test1 = J.test1() + if (test1 != 1) return "fail 1: $test1 != 1" + + val test2: X> = J.test2() as X> + + return test2.p1 + test2.p2.value +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/genericSignature/kt11121.kt b/compiler/testData/codegen/boxWithStdlib/reflection/genericSignature/kt11121.kt new file mode 100644 index 00000000000..5dfabb2cd86 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/genericSignature/kt11121.kt @@ -0,0 +1,23 @@ +class B + +interface A> { + + fun p(p: T): T { + return p + } + + val T.z : T? + get() = null +} + + +fun box(): String { + val defaultImpls = Class.forName("A\$DefaultImpls") + val declaredMethod = defaultImpls.getDeclaredMethod("p", A::class.java, Any::class.java) + if (declaredMethod.toGenericString() != "public static T A\$DefaultImpls.p(A,T)") return "fail 1: ${declaredMethod.toGenericString()}" + + val declaredProperty = defaultImpls.getDeclaredMethod("getZ", A::class.java, Any::class.java) + if (declaredProperty.toGenericString() != "public static T A\$DefaultImpls.getZ(A,T)") return "fail 2: ${declaredProperty.toGenericString()}" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/writeSignature/defaultImpls/functionTypeParameterClash.kt b/compiler/testData/writeSignature/defaultImpls/functionTypeParameterClash.kt new file mode 100644 index 00000000000..647b7ce0801 --- /dev/null +++ b/compiler/testData/writeSignature/defaultImpls/functionTypeParameterClash.kt @@ -0,0 +1,13 @@ +class B + +interface A> { + + fun p(p: T): T { + return p + } + +} + +// method: A$DefaultImpls::p +// jvm signature: (LA;Ljava/lang/Object;)Ljava/lang/Object; +// generic signature: ;T:Ljava/lang/Object;L:Ljava/lang/Object;>(LA;TT;)TT; diff --git a/compiler/testData/writeSignature/defaultImpls/functionTypeParameterClashWith_I.kt b/compiler/testData/writeSignature/defaultImpls/functionTypeParameterClashWith_I.kt new file mode 100644 index 00000000000..8c06675d234 --- /dev/null +++ b/compiler/testData/writeSignature/defaultImpls/functionTypeParameterClashWith_I.kt @@ -0,0 +1,13 @@ +class B + +interface A, T_I1: T> { + + fun p(p: T): T { + return p + } + +} + +// method: A$DefaultImpls::p +// jvm signature: (LA;Ljava/lang/Object;)Ljava/lang/Object; +// generic signature: ;T_I1::TT_I2;T:Ljava/lang/Object;L:Ljava/lang/Object;>(LA;TT;)TT; diff --git a/compiler/testData/writeSignature/defaultImpls/propertyTypeParameterClash.kt b/compiler/testData/writeSignature/defaultImpls/propertyTypeParameterClash.kt new file mode 100644 index 00000000000..645cb2b4cff --- /dev/null +++ b/compiler/testData/writeSignature/defaultImpls/propertyTypeParameterClash.kt @@ -0,0 +1,11 @@ +class B + +interface A> { + + val T.z: T? + get() = null +} + +// method: A$DefaultImpls::getZ +// jvm signature: (LA;Ljava/lang/Object;)Ljava/lang/Object; +// generic signature: ;T:Ljava/lang/Object;>(LA;TT;)TT; diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJavaCodegenTestGenerated.java index 6ddf8968439..69c3d9f3ec9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithJavaCodegenTestGenerated.java @@ -601,6 +601,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege doTest(fileName); } + @TestMetadata("defaultImplsGenericSignature.kt") + public void testDefaultImplsGenericSignature() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/defaultImplsGenericSignature.kt"); + doTest(fileName); + } + @TestMetadata("functionReferenceErasedToKFunction.kt") public void testFunctionReferenceErasedToKFunction() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/functionReferenceErasedToKFunction.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java index 5b8d11059cd..3d6ea539fba 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxWithStdlibCodegenTestGenerated.java @@ -3819,6 +3819,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode doTestWithStdlib(fileName); } + @TestMetadata("kt11121.kt") + public void testKt11121() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/genericSignature/kt11121.kt"); + doTestWithStdlib(fileName); + } + @TestMetadata("kt5112.kt") public void testKt5112() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/genericSignature/kt5112.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/WriteSignatureTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/WriteSignatureTestGenerated.java index 76f8e8b8359..9ed624bb62c 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/WriteSignatureTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/WriteSignatureTestGenerated.java @@ -482,6 +482,33 @@ public class WriteSignatureTestGenerated extends AbstractWriteSignatureTest { } } + @TestMetadata("compiler/testData/writeSignature/defaultImpls") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DefaultImpls extends AbstractWriteSignatureTest { + public void testAllFilesPresentInDefaultImpls() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/writeSignature/defaultImpls"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("functionTypeParameterClash.kt") + public void testFunctionTypeParameterClash() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/defaultImpls/functionTypeParameterClash.kt"); + doTest(fileName); + } + + @TestMetadata("functionTypeParameterClashWith_I.kt") + public void testFunctionTypeParameterClashWith_I() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/defaultImpls/functionTypeParameterClashWith_I.kt"); + doTest(fileName); + } + + @TestMetadata("propertyTypeParameterClash.kt") + public void testPropertyTypeParameterClash() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/writeSignature/defaultImpls/propertyTypeParameterClash.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/writeSignature/nothing") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)