Fix type deserialization failure when type alias was not found
It's crucial for decompilation as declarations are always being deserialized with no dependencies #KT-12832 Fixed
This commit is contained in:
+79
-19
@@ -18,20 +18,21 @@ package org.jetbrains.kotlin.serialization.deserialization
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.AbstractTypeAliasDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorBase
|
||||
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.types.ClassTypeConstructorImpl
|
||||
import org.jetbrains.kotlin.types.TypeConstructor
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.toReadOnlyList
|
||||
import org.jetbrains.kotlin.storage.getValue
|
||||
import org.jetbrains.kotlin.types.*
|
||||
|
||||
class NotFoundClasses(private val storageManager: StorageManager, private val module: ModuleDescriptor) {
|
||||
/**
|
||||
@@ -44,6 +45,25 @@ class NotFoundClasses(private val storageManager: StorageManager, private val mo
|
||||
}
|
||||
|
||||
private val classes = storageManager.createMemoizedFunction<ClassRequest, ClassDescriptor> { request ->
|
||||
computeClassifier(request, {
|
||||
owner, name, isInner, numberOfTypeParametersCount ->
|
||||
MockClassDescriptor(storageManager, owner, name, isInner, numberOfTypeParametersCount)
|
||||
})
|
||||
}
|
||||
|
||||
private val typeAliases = storageManager.createMemoizedFunction<ClassRequest, TypeAliasDescriptor> { request ->
|
||||
computeClassifier(request, {
|
||||
owner, name, isInner, numberOfTypeParametersCount ->
|
||||
MockTypeAliasDescriptor(storageManager, owner, name, numberOfTypeParametersCount)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Uncomment this when KT-12871 is fixed
|
||||
// private typealias ConstructorFunction<D> = (DeclarationDescriptor, Name, isInner: Boolean, numberOfTypeParametersCount: Int) -> D
|
||||
private fun <D> computeClassifier(
|
||||
request: ClassRequest,
|
||||
constructor: (DeclarationDescriptor, Name, isInner: Boolean, numberOfTypeParametersCount: Int) -> D
|
||||
): D {
|
||||
val (classId, typeParametersCount) = request
|
||||
|
||||
if (classId.isLocal) {
|
||||
@@ -57,21 +77,17 @@ class NotFoundClasses(private val storageManager: StorageManager, private val mo
|
||||
// Treat a class with a nested ClassId as inner for simplicity, otherwise the outer type cannot have generic arguments
|
||||
val isInner = classId.isNestedClass
|
||||
|
||||
MockClassDescriptor(storageManager, container, classId.shortClassName, isInner, typeParametersCount.firstOrNull() ?: 0)
|
||||
return constructor(container, classId.shortClassName, isInner, typeParametersCount.firstOrNull() ?: 0)
|
||||
}
|
||||
|
||||
class MockClassDescriptor(
|
||||
class MockClassDescriptor internal constructor(
|
||||
storageManager: StorageManager,
|
||||
container: DeclarationDescriptor,
|
||||
name: Name,
|
||||
private val isInner: Boolean,
|
||||
numberOfDeclaredTypeParameters: Int
|
||||
) : ClassDescriptorBase(storageManager, container, name, SourceElement.NO_SOURCE) {
|
||||
private val typeParameters = (1..numberOfDeclaredTypeParameters).map { index ->
|
||||
TypeParameterDescriptorImpl.createWithDefaultBound(
|
||||
this, Annotations.EMPTY, false, Variance.INVARIANT, Name.identifier("T$index"), index
|
||||
)
|
||||
}
|
||||
private val typeParameters = createTypeParameters(this, numberOfDeclaredTypeParameters)
|
||||
|
||||
private val typeConstructor = ClassTypeConstructorImpl(
|
||||
this, Annotations.EMPTY, /* isFinal = */ true, typeParameters, setOf(module.builtIns.anyType)
|
||||
@@ -97,6 +113,32 @@ class NotFoundClasses(private val storageManager: StorageManager, private val mo
|
||||
override fun toString() = "class $name (not found)"
|
||||
}
|
||||
|
||||
private class MockTypeAliasDescriptor(
|
||||
storageManager: StorageManager,
|
||||
containingDeclaration: DeclarationDescriptor,
|
||||
name: Name,
|
||||
numberOfDeclaredTypeParameters: Int
|
||||
) : AbstractTypeAliasDescriptor(containingDeclaration, Annotations.EMPTY, name, SourceElement.NO_SOURCE, Visibilities.PUBLIC) {
|
||||
init {
|
||||
initialize(createTypeParameters(this, numberOfDeclaredTypeParameters))
|
||||
}
|
||||
|
||||
private val constructorTypeParameters by storageManager.createLazyValue { computeConstructorTypeParameters() }
|
||||
|
||||
override fun getTypeConstructorTypeParameters() = constructorTypeParameters
|
||||
|
||||
// We don't have enough information about underlying type, so just take nullable Any?
|
||||
// Anyway it should not used extensively, because not found type aliases are only used for type abbreviations
|
||||
override val underlyingType: SimpleType
|
||||
get() = builtIns.nullableAnyType
|
||||
override val expandedType: SimpleType
|
||||
get() = builtIns.nullableAnyType
|
||||
|
||||
override fun substitute(substitutor: TypeSubstitutor) = this
|
||||
|
||||
override fun toString() = "MockTypeAliasDescriptor[$fqNameUnsafe]"
|
||||
}
|
||||
|
||||
// We create different ClassDescriptor instances for types with the same ClassId but different number of type arguments.
|
||||
// (This may happen when a class with the same FQ name is instantiated with different type arguments in different modules.)
|
||||
// It's better than creating just one descriptor because otherwise would fail in multiple places where it's asserted that
|
||||
@@ -105,17 +147,35 @@ class NotFoundClasses(private val storageManager: StorageManager, private val mo
|
||||
return classes(ClassRequest(classId, typeParametersCount))
|
||||
}
|
||||
|
||||
fun get(proto: ProtoBuf.Type, nameResolver: NameResolver, typeTable: TypeTable): TypeConstructor {
|
||||
fun getClass(proto: ProtoBuf.Type, nameResolver: NameResolver, typeTable: TypeTable): TypeConstructor {
|
||||
val classId = nameResolver.getClassId(proto.className)
|
||||
val typeParametersCount = generateSequence(proto) { it.outerType(typeTable) }.map { it.argumentCount }.toMutableList()
|
||||
val classNestingLevel = generateSequence(classId) { if (it.isNestedClass) it.outerClassId else null }.count()
|
||||
while (typeParametersCount.size < classNestingLevel) {
|
||||
typeParametersCount.add(0)
|
||||
}
|
||||
return getOrCreateClass(classId, typeParametersCount.toReadOnlyList()).typeConstructor
|
||||
return getOrCreateClass(classId, computeTypeParametersCount(classId, proto, typeTable)).typeConstructor
|
||||
}
|
||||
|
||||
fun get(classId: ClassId, typeParametersCount: List<Int>): TypeConstructor {
|
||||
fun getClass(classId: ClassId, typeParametersCount: List<Int>): TypeConstructor {
|
||||
return getOrCreateClass(classId, typeParametersCount).typeConstructor
|
||||
}
|
||||
|
||||
fun getTypeAlias(proto: ProtoBuf.Type, nameResolver: NameResolver, typeTable: TypeTable): TypeConstructor {
|
||||
val classId = nameResolver.getClassId(proto.typeAliasName)
|
||||
return typeAliases(ClassRequest(classId, computeTypeParametersCount(classId, proto, typeTable))).typeConstructor
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTypeParameters(
|
||||
classifierDescriptor: ClassifierDescriptor,
|
||||
numberOfDeclaredTypeParameters: Int
|
||||
) = (1..numberOfDeclaredTypeParameters).map { index ->
|
||||
TypeParameterDescriptorImpl.createWithDefaultBound(
|
||||
classifierDescriptor, Annotations.EMPTY, false, Variance.INVARIANT, Name.identifier("T$index"), index
|
||||
)
|
||||
}
|
||||
|
||||
private fun computeTypeParametersCount(classId: ClassId, proto: ProtoBuf.Type, typeTable: TypeTable): List<Int> {
|
||||
val typeParametersCount = generateSequence(proto) { it.outerType(typeTable) }.map { it.argumentCount }.toMutableList()
|
||||
val classNestingLevel = generateSequence(classId) { if (it.isNestedClass) it.outerClassId else null }.count()
|
||||
while (typeParametersCount.size < classNestingLevel) {
|
||||
typeParametersCount.add(0)
|
||||
}
|
||||
return typeParametersCount
|
||||
}
|
||||
|
||||
+2
-2
@@ -96,7 +96,7 @@ class TypeDeserializer(
|
||||
when {
|
||||
proto.hasClassName() -> {
|
||||
classDescriptors(proto.className)?.typeConstructor
|
||||
?: c.components.notFoundClasses.get(proto, c.nameResolver, c.typeTable)
|
||||
?: c.components.notFoundClasses.getClass(proto, c.nameResolver, c.typeTable)
|
||||
}
|
||||
proto.hasTypeParameter() ->
|
||||
typeParameterTypeConstructor(proto.typeParameter)
|
||||
@@ -109,7 +109,7 @@ class TypeDeserializer(
|
||||
}
|
||||
proto.hasTypeAliasName() -> {
|
||||
typeAliasDescriptors(proto.typeAliasName)?.typeConstructor
|
||||
?: TODO("not found type aliases")
|
||||
?: c.components.notFoundClasses.getTypeAlias(proto, c.nameResolver, c.typeTable)
|
||||
}
|
||||
else -> ErrorUtils.createErrorTypeConstructor("Unknown type")
|
||||
}
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ fun ModuleDescriptor.findNonGenericClassAcrossDependencies(classId: ClassId, not
|
||||
// Take a list of N zeros, where N is the number of class names in the given ClassId
|
||||
val typeParametersCount = generateSequence(classId) { if (it.isNestedClass) it.outerClassId else null }.map { 0 }.toList()
|
||||
|
||||
return notFoundClasses.get(classId, typeParametersCount).declarationDescriptor as ClassDescriptor
|
||||
return notFoundClasses.getClass(classId, typeParametersCount).declarationDescriptor as ClassDescriptor
|
||||
}
|
||||
|
||||
fun ModuleDescriptor.findTypeAliasAcrossModuleDependencies(classId: ClassId): TypeAliasDescriptor? {
|
||||
|
||||
Reference in New Issue
Block a user