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:
Denis Zharkov
2016-06-27 14:55:56 +03:00
parent 15ce4f8856
commit 067fe35b72
11 changed files with 131 additions and 23 deletions
@@ -140,7 +140,7 @@ class JavaTypeResolver(
// Note that this makes MISSING_DEPENDENCY_CLASS diagnostic messages not as precise as they could be in some corner cases.
private fun createNotFoundClass(javaType: JavaClassifierType): TypeConstructor {
val classId = parseCanonicalFqNameIgnoringTypeArguments(javaType.canonicalText)
return c.components.deserializedDescriptorResolver.components.notFoundClasses.get(classId, listOf(0))
return c.components.deserializedDescriptorResolver.components.notFoundClasses.getClass(classId, listOf(0))
}
private fun mapKotlinClass(javaType: JavaClassifierType, attr: JavaTypeAttributes, fqName: FqName): ClassDescriptor? {
@@ -36,6 +36,7 @@ public class TypeParameterDescriptorImpl extends AbstractTypeParameterDescriptor
@Nullable
private final Function1<KotlinType, Void> reportCycleError;
@NotNull
public static TypeParameterDescriptor createWithDefaultBound(
@NotNull DeclarationDescriptor containingDeclaration,
@NotNull Annotations annotations,
@@ -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
}
@@ -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")
}
@@ -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? {
@@ -0,0 +1,10 @@
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package test
public final class TypeAliases public constructor() {
public final fun foo(a: dependency.A /* = () -> kotlin.Unit */, b: test.TypeAliases.B /* = (dependency.A /* = () -> kotlin.Unit */) -> kotlin.Unit */, ta: test.Outer<kotlin.String, kotlin.Double>.Inner<kotlin.Int>.TA<kotlin.Boolean> /* = kotlin.collections.Map<kotlin.collections.Map<kotlin.String, out kotlin.Double>, kotlin.collections.Map<kotlin.Int, out kotlin.Boolean>> */): kotlin.Unit { /* compiled code */ }
public typealias B = (dependency.A) -> kotlin.Unit
}
@@ -0,0 +1,7 @@
package dependency
typealias A = () -> Unit
fun foo(a: A) {
a.invoke()
}
@@ -0,0 +1,17 @@
package test
import dependency.*
class Outer<E, F> {
inner class Inner<G> {
typealias TA<H> = Map<Map<E, F>, Map<G, H>>
}
}
class TypeAliases {
typealias B = (A) -> Unit
fun foo(a: A, b: B, ta: Outer<String, Double>.Inner<Int>.TA<Boolean>) {
b.invoke(a)
}
}
@@ -0,0 +1 @@
// TARGET_BACKEND: JVM
@@ -61,6 +61,12 @@ public class CommonDecompiledTextFromJsMetadataTestGenerated extends AbstractCom
doTest(fileName);
}
@TestMetadata("TypeAliases")
public void ignoredTypeAliases() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/decompiledText/TypeAliases/");
doTest(fileName);
}
public void testAllFilesPresentInDecompiledText() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/decompiler/decompiledText"), Pattern.compile("^([^\\.]+)$"), true);
}
@@ -137,4 +137,10 @@ public class CommonDecompiledTextTestGenerated extends AbstractCommonDecompiledT
doTest(fileName);
}
@TestMetadata("TypeAliases")
public void testTypeAliases() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/decompiledText/TypeAliases/");
doTest(fileName);
}
}