FIR: in computeJvmDescriptor, convert type parameter bounds too

Note the sheer amount of TODOs, though - I'm honestly surprised that
function works at all.
This commit is contained in:
pyos
2021-09-01 13:43:44 +02:00
committed by teamcityserver
parent a2e2545e3f
commit bbc476f44e
@@ -25,6 +25,8 @@ fun FirFunction.computeJvmSignature(typeConversion: (FirTypeRef) -> ConeKotlinTy
return SignatureBuildingComponents.signature(containingClass.classId, computeJvmDescriptor(typeConversion = typeConversion)) return SignatureBuildingComponents.signature(containingClass.classId, computeJvmDescriptor(typeConversion = typeConversion))
} }
// TODO: `typeConversion` is only used for converting Java types into cone types, but shouldn't it be trivial
// to construct a JVM descriptor from a Java type directly? The question is how to make the two paths consistent...
fun FirFunction.computeJvmDescriptor( fun FirFunction.computeJvmDescriptor(
customName: String? = null, customName: String? = null,
includeReturnType: Boolean = true, includeReturnType: Boolean = true,
@@ -42,7 +44,7 @@ fun FirFunction.computeJvmDescriptor(
append("(") append("(")
for (parameter in valueParameters) { for (parameter in valueParameters) {
typeConversion(parameter.returnTypeRef)?.let(this::appendConeType) typeConversion(parameter.returnTypeRef)?.let { appendConeType(it, typeConversion) }
} }
append(")") append(")")
@@ -50,7 +52,7 @@ fun FirFunction.computeJvmDescriptor(
if (this@computeJvmDescriptor !is FirSimpleFunction || returnTypeRef.isVoid()) { if (this@computeJvmDescriptor !is FirSimpleFunction || returnTypeRef.isVoid()) {
append("V") append("V")
} else { } else {
typeConversion(returnTypeRef)?.let(this::appendConeType) typeConversion(returnTypeRef)?.let { appendConeType(it, typeConversion) }
} }
} }
} }
@@ -66,7 +68,7 @@ private val PRIMITIVE_TYPE_SIGNATURE: Map<String, String> = mapOf(
"Double" to "D", "Double" to "D",
) )
private fun StringBuilder.appendConeType(coneType: ConeKotlinType) { private fun StringBuilder.appendConeType(coneType: ConeKotlinType, typeConversion: (FirTypeRef) -> ConeKotlinType?) {
(coneType as? ConeClassLikeType)?.let { (coneType as? ConeClassLikeType)?.let {
val classId = it.lookupTag.classId val classId = it.lookupTag.classId
if (classId.packageFqName.toString() == "kotlin") { if (classId.packageFqName.toString() == "kotlin") {
@@ -79,13 +81,14 @@ private fun StringBuilder.appendConeType(coneType: ConeKotlinType) {
fun appendClassLikeType(type: ConeClassLikeType) { fun appendClassLikeType(type: ConeClassLikeType) {
val baseClassId = type.lookupTag.classId val baseClassId = type.lookupTag.classId
// TODO: what about primitive arrays?
val classId = JavaToKotlinClassMap.mapKotlinToJava(baseClassId.asSingleFqName().toUnsafe()) ?: baseClassId val classId = JavaToKotlinClassMap.mapKotlinToJava(baseClassId.asSingleFqName().toUnsafe()) ?: baseClassId
if (classId == StandardClassIds.Array) { if (classId == StandardClassIds.Array) {
append("[") append("[")
type.typeArguments.forEach { typeArg -> type.typeArguments.forEach { typeArg ->
when (typeArg) { when (typeArg) {
ConeStarProjection -> append("*") ConeStarProjection -> append("*")
is ConeKotlinTypeProjection -> appendConeType(typeArg.type) is ConeKotlinTypeProjection -> appendConeType(typeArg.type, typeConversion)
} }
} }
} else { } else {
@@ -97,28 +100,29 @@ private fun StringBuilder.appendConeType(coneType: ConeKotlinType) {
} }
} }
if (coneType is ConeClassErrorType) return
when (coneType) { when (coneType) {
is ConeClassErrorType -> Unit // TODO: just skipping it seems wrong
is ConeClassLikeType -> { is ConeClassLikeType -> {
appendClassLikeType(coneType) appendClassLikeType(coneType)
} }
is ConeTypeParameterType -> { is ConeTypeParameterType -> {
val representative = coneType.lookupTag.typeParameterSymbol.fir.bounds.firstOrNull { // TODO: 1. unannotated bounds are probably flexible, so this isn't right;
it.coneType is ConeClassLikeType // 2. shouldn't this always take the first bound and recurse if it's also a type parameter?
} coneType.lookupTag.typeParameterSymbol.fir.bounds.firstNotNullOfOrNull {
if (representative == null || representative is FirImplicitNullableAnyTypeRef || representative is FirImplicitAnyTypeRef) { val converted = typeConversion(it)
append("Ljava/lang/Object;") if (converted is ConeClassLikeType) it to converted else null
} else { }?.let { (firBound, coneBound) ->
appendClassLikeType(representative.coneTypeUnsafe()) // TODO: pretty sure Java type conversion does not produce either of these
if (firBound !is FirImplicitNullableAnyTypeRef && firBound !is FirImplicitAnyTypeRef) {
appendClassLikeType(coneBound)
return
}
} }
append("Ljava/lang/Object;")
} }
is ConeDefinitelyNotNullType -> { is ConeDefinitelyNotNullType -> appendConeType(coneType.original, typeConversion)
appendConeType(coneType.original) is ConeFlexibleType -> appendConeType(coneType.lowerBound, typeConversion)
} else -> Unit // TODO: throw an error? should check that Java type conversion/enhancement can only produce these cone types
is ConeFlexibleType -> {
appendConeType(coneType.lowerBound)
}
else -> {}
} }
} }