From 067fe35b72c6a495fbb0b80f479eabcda059d237 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 27 Jun 2016 14:55:56 +0300 Subject: [PATCH] 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 --- .../load/java/lazy/types/JavaTypeResolver.kt | 2 +- .../impl/TypeParameterDescriptorImpl.java | 1 + .../deserialization/NotFoundClasses.kt | 98 +++++++++++++++---- .../deserialization/TypeDeserializer.kt | 4 +- .../deserialization/findClassInModule.kt | 2 +- .../decompiledText/TypeAliases.expected.kt | 10 ++ .../decompiledText/TypeAliases/Dependency.kt | 7 ++ .../decompiledText/TypeAliases/TypeAliases.kt | 17 ++++ .../decompiledText/TypeAliases/directives.txt | 1 + ...mpiledTextFromJsMetadataTestGenerated.java | 6 ++ .../CommonDecompiledTextTestGenerated.java | 6 ++ 11 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 idea/testData/decompiler/decompiledText/TypeAliases.expected.kt create mode 100644 idea/testData/decompiler/decompiledText/TypeAliases/Dependency.kt create mode 100644 idea/testData/decompiler/decompiledText/TypeAliases/TypeAliases.kt create mode 100644 idea/testData/decompiler/decompiledText/TypeAliases/directives.txt diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt index 48eb85525aa..7cf15b70dde 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/JavaTypeResolver.kt @@ -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? { diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/TypeParameterDescriptorImpl.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/TypeParameterDescriptorImpl.java index 85fbcdf7719..da6cbb1298f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/TypeParameterDescriptorImpl.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/TypeParameterDescriptorImpl.java @@ -36,6 +36,7 @@ public class TypeParameterDescriptorImpl extends AbstractTypeParameterDescriptor @Nullable private final Function1 reportCycleError; + @NotNull public static TypeParameterDescriptor createWithDefaultBound( @NotNull DeclarationDescriptor containingDeclaration, @NotNull Annotations annotations, diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/NotFoundClasses.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/NotFoundClasses.kt index e2bd71059a6..c479b6a9f70 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/NotFoundClasses.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/NotFoundClasses.kt @@ -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 { request -> + computeClassifier(request, { + owner, name, isInner, numberOfTypeParametersCount -> + MockClassDescriptor(storageManager, owner, name, isInner, numberOfTypeParametersCount) + }) + } + + private val typeAliases = storageManager.createMemoizedFunction { request -> + computeClassifier(request, { + owner, name, isInner, numberOfTypeParametersCount -> + MockTypeAliasDescriptor(storageManager, owner, name, numberOfTypeParametersCount) + }) + } + + // TODO: Uncomment this when KT-12871 is fixed + // private typealias ConstructorFunction = (DeclarationDescriptor, Name, isInner: Boolean, numberOfTypeParametersCount: Int) -> D + private fun 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): TypeConstructor { + fun getClass(classId: ClassId, typeParametersCount: List): 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 { + 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 } diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt index 85c0af3beb6..dca11344e9d 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt @@ -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") } diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/findClassInModule.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/findClassInModule.kt index 7afce11c2fa..2767a9f4a8a 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/findClassInModule.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/findClassInModule.kt @@ -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? { diff --git a/idea/testData/decompiler/decompiledText/TypeAliases.expected.kt b/idea/testData/decompiler/decompiledText/TypeAliases.expected.kt new file mode 100644 index 00000000000..ec608b2128e --- /dev/null +++ b/idea/testData/decompiler/decompiledText/TypeAliases.expected.kt @@ -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.Inner.TA /* = kotlin.collections.Map, kotlin.collections.Map> */): kotlin.Unit { /* compiled code */ } + + public typealias B = (dependency.A) -> kotlin.Unit +} diff --git a/idea/testData/decompiler/decompiledText/TypeAliases/Dependency.kt b/idea/testData/decompiler/decompiledText/TypeAliases/Dependency.kt new file mode 100644 index 00000000000..f6e5c1108e8 --- /dev/null +++ b/idea/testData/decompiler/decompiledText/TypeAliases/Dependency.kt @@ -0,0 +1,7 @@ +package dependency + +typealias A = () -> Unit + +fun foo(a: A) { + a.invoke() +} diff --git a/idea/testData/decompiler/decompiledText/TypeAliases/TypeAliases.kt b/idea/testData/decompiler/decompiledText/TypeAliases/TypeAliases.kt new file mode 100644 index 00000000000..2e2b7168473 --- /dev/null +++ b/idea/testData/decompiler/decompiledText/TypeAliases/TypeAliases.kt @@ -0,0 +1,17 @@ +package test + +import dependency.* + +class Outer { + inner class Inner { + typealias TA = Map, Map> + } +} + +class TypeAliases { + + typealias B = (A) -> Unit + fun foo(a: A, b: B, ta: Outer.Inner.TA) { + b.invoke(a) + } +} diff --git a/idea/testData/decompiler/decompiledText/TypeAliases/directives.txt b/idea/testData/decompiler/decompiledText/TypeAliases/directives.txt new file mode 100644 index 00000000000..7beb2941e0c --- /dev/null +++ b/idea/testData/decompiler/decompiledText/TypeAliases/directives.txt @@ -0,0 +1 @@ +// TARGET_BACKEND: JVM diff --git a/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/CommonDecompiledTextFromJsMetadataTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/CommonDecompiledTextFromJsMetadataTestGenerated.java index a2976949127..2b9a0c548b8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/CommonDecompiledTextFromJsMetadataTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/CommonDecompiledTextFromJsMetadataTestGenerated.java @@ -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); } diff --git a/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/CommonDecompiledTextTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/CommonDecompiledTextTestGenerated.java index ed5d1f19bce..f16b5780c15 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/CommonDecompiledTextTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/CommonDecompiledTextTestGenerated.java @@ -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); + } + }