Write additional type parameters for DefaultImpls methods, fix for KT-11121: BadClassFile exception for interface implemented generic properties
#KT-11121 Fixed
This commit is contained in:
@@ -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<TypeParameterDescriptor> typeParameters, @NotNull JvmSignatureWriter sw) {
|
||||
if (sw.skipGenericSignature()) return;
|
||||
for (TypeParameterDescriptor typeParameter : typeParameters) {
|
||||
|
||||
@@ -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<TypeParameterDescriptor>)
|
||||
|
||||
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<TypeParameterDescriptor>()
|
||||
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
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// FILE: J.java
|
||||
|
||||
public class J {
|
||||
|
||||
public static int test1() {
|
||||
A<String, B<String>> x = new X<String, B<String>>("O", new B<String>("K"));
|
||||
return A.DefaultImpls.test1(x, 1, 1.0);
|
||||
}
|
||||
|
||||
|
||||
public static A<String, B<String>> test2(){
|
||||
X<String, B<String>> x = new X<String, B<String>>("O", new B<String>("K"));
|
||||
return A.DefaultImpls.test2(x, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: K.kt
|
||||
|
||||
class B<T>(val value: T)
|
||||
|
||||
interface A<T, Y : B<T>> {
|
||||
|
||||
fun <T, L> test1(p: T, z: L): T {
|
||||
return p
|
||||
}
|
||||
|
||||
fun <L> test2(p: L): A<T, Y> {
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class X<T, Y : B<T>>(val p1: T, val p2: Y) : A<T, Y> {
|
||||
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val test1 = J.test1()
|
||||
if (test1 != 1) return "fail 1: $test1 != 1"
|
||||
|
||||
val test2: X<String, B<String>> = J.test2() as X<String, B<String>>
|
||||
|
||||
return test2.p1 + test2.p2.value
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
class B<M>
|
||||
|
||||
interface A<T, Y : B<T>> {
|
||||
|
||||
fun <T, L> p(p: T): T {
|
||||
return p
|
||||
}
|
||||
|
||||
val <T> 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_I1,Y,T,L> T A\$DefaultImpls.p(A<T_I1, Y>,T)") return "fail 1: ${declaredMethod.toGenericString()}"
|
||||
|
||||
val declaredProperty = defaultImpls.getDeclaredMethod("getZ", A::class.java, Any::class.java)
|
||||
if (declaredProperty.toGenericString() != "public static <T_I1,Y,T> T A\$DefaultImpls.getZ(A<T_I1, Y>,T)") return "fail 2: ${declaredProperty.toGenericString()}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
class B<M>
|
||||
|
||||
interface A<T, Y : B<T>> {
|
||||
|
||||
fun <T, L> p(p: T): T {
|
||||
return p
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// method: A$DefaultImpls::p
|
||||
// jvm signature: (LA;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
// generic signature: <T_I1:Ljava/lang/Object;Y:LB<TT_I1;>;T:Ljava/lang/Object;L:Ljava/lang/Object;>(LA<TT_I1;TY;>;TT;)TT;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
class B<M>
|
||||
|
||||
interface A<T, Y : B<T>, T_I1: T> {
|
||||
|
||||
fun <T, L> p(p: T): T {
|
||||
return p
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// method: A$DefaultImpls::p
|
||||
// jvm signature: (LA;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
// generic signature: <T_I2:Ljava/lang/Object;Y:LB<TT_I2;>;T_I1::TT_I2;T:Ljava/lang/Object;L:Ljava/lang/Object;>(LA<TT_I2;TY;TT_I1;>;TT;)TT;
|
||||
@@ -0,0 +1,11 @@
|
||||
class B<M>
|
||||
|
||||
interface A<T, Y : B<T>> {
|
||||
|
||||
val <T> T.z: T?
|
||||
get() = null
|
||||
}
|
||||
|
||||
// method: A$DefaultImpls::getZ
|
||||
// jvm signature: (LA;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
// generic signature: <T_I1:Ljava/lang/Object;Y:LB<TT_I1;>;T:Ljava/lang/Object;>(LA<TT_I1;TY;>;TT;)TT;
|
||||
@@ -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");
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user