From 341f7c348a2d043227477c032a87116aff0705ac Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Thu, 13 Sep 2018 15:43:34 +0300 Subject: [PATCH] Fix memory leak in KotlinBuiltins related to unsigned types Having a map from ModuleDescriptor leads to modules leakage Especially, it's critical for DefaultBuiltins which is used in JS ModuleDescriptor's instances were leaked there forever until the daemon dies (up to 350M while compiling Kotlin project itself) --- .../kotlin/builtins/KotlinBuiltIns.java | 59 ++++++++++--------- .../jetbrains/kotlin/builtins/UnsignedType.kt | 23 ++++++-- .../kotlin/resolve/DescriptorUtils.kt | 4 +- 3 files changed, 53 insertions(+), 33 deletions(-) diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 15f92fa2268..c81a6ca15ee 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqNameUnsafe; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.resolve.DescriptorUtils; +import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.resolve.scopes.ChainedMemberScope; import org.jetbrains.kotlin.resolve.scopes.MemberScope; import org.jetbrains.kotlin.storage.MemoizedFunctionToNotNull; @@ -64,7 +65,6 @@ public abstract class KotlinBuiltIns { private ModuleDescriptorImpl builtInsModule; private final NotNullLazyValue primitives; - private final MemoizedFunctionToNotNull unsignedPrimitives; private final NotNullLazyValue packageFragments; private final MemoizedFunctionToNotNull builtInClassesByName; @@ -114,29 +114,6 @@ public abstract class KotlinBuiltIns { } }); - this.unsignedPrimitives = storageManager.createMemoizedFunction(new Function1() { - @Override - public UnsignedPrimitives invoke(ModuleDescriptor module) { - Map unsignedKotlinTypeToKotlinArrayType = new HashMap(); - Map kotlinArrayTypeToUnsignedKotlinType = new HashMap(); - for (UnsignedType unsigned : UnsignedType.values()) { - ClassDescriptor descriptor = FindClassInModuleKt.findClassAcrossModuleDependencies(module, unsigned.getClassId()); - if (descriptor == null) continue; - - ClassDescriptor arrayDescriptor = - FindClassInModuleKt.findClassAcrossModuleDependencies(module, unsigned.getArrayClassId()); - if (arrayDescriptor == null) continue; - - SimpleType type = descriptor.getDefaultType(); - SimpleType arrayType = arrayDescriptor.getDefaultType(); - - unsignedKotlinTypeToKotlinArrayType.put(type, arrayType); - kotlinArrayTypeToUnsignedKotlinType.put(arrayType, type); - } - return new UnsignedPrimitives(unsignedKotlinTypeToKotlinArrayType, kotlinArrayTypeToUnsignedKotlinType); - } - }); - this.builtInClassesByName = storageManager.createMemoizedFunction(new Function1() { @Override public ClassDescriptor invoke(Name name) { @@ -824,8 +801,7 @@ public abstract class KotlinBuiltIns { ModuleDescriptor module = DescriptorUtils.getContainingModuleOrNull(notNullArrayType); if (module != null) { - //noinspection SuspiciousMethodCalls - SimpleType unsignedType = unsignedPrimitives.invoke(module).kotlinArrayTypeToUnsignedKotlinType.get(notNullArrayType); + KotlinType unsignedType = getElementTypeForUnsignedArray(notNullArrayType, module); if (unsignedType != null) return unsignedType; } @@ -833,6 +809,24 @@ public abstract class KotlinBuiltIns { throw new IllegalStateException("not array: " + arrayType); } + @Nullable + private static KotlinType getElementTypeForUnsignedArray(@NotNull KotlinType notNullArrayType, @NotNull ModuleDescriptor module) { + ClassifierDescriptor descriptor = notNullArrayType.getConstructor().getDeclarationDescriptor(); + if (descriptor == null) return null; + if (!UnsignedTypes.INSTANCE.isShortNameOfUnsignedArray(descriptor.getName())) return null; + + ClassId arrayClassId = DescriptorUtilsKt.getClassId(descriptor); + if (arrayClassId == null) return null; + + ClassId elementClassId = UnsignedTypes.INSTANCE.getUnsignedClassIdByArrayClassId(arrayClassId); + if (elementClassId == null) return null; + + ClassDescriptor elementClassDescriptor = FindClassInModuleKt.findClassAcrossModuleDependencies(module, elementClassId); + if (elementClassDescriptor == null) return null; + + return elementClassDescriptor.getDefaultType(); + } + @NotNull public SimpleType getPrimitiveArrayKotlinType(@NotNull PrimitiveType primitiveType) { return primitives.invoke().primitiveTypeToArrayKotlinType.get(primitiveType); @@ -847,10 +841,21 @@ public abstract class KotlinBuiltIns { if (primitiveArray != null) return primitiveArray; if (UnsignedTypes.INSTANCE.isUnsignedType(kotlinType)) { + if (TypeUtils.isNullableType(kotlinType)) return null; + ModuleDescriptor module = DescriptorUtils.getContainingModuleOrNull(kotlinType); if (module == null) return null; - return unsignedPrimitives.invoke(module).unsignedKotlinTypeToKotlinArrayType.get(kotlinType); + ClassId unsignedClassId = DescriptorUtilsKt.getClassId(kotlinType.getConstructor().getDeclarationDescriptor()); + assert unsignedClassId != null : "unsignedClassId should not be null for unsigned type " + kotlinType; + + ClassId arrayClassId = UnsignedTypes.INSTANCE.getUnsignedArrayClassIdByUnsignedClassId(unsignedClassId); + assert arrayClassId != null : "arrayClassId should not be null for unsigned type " + unsignedClassId; + + ClassDescriptor arrayClassDescriptor = FindClassInModuleKt.findClassAcrossModuleDependencies(module, arrayClassId); + if (arrayClassDescriptor == null) return null; + + return arrayClassDescriptor.getDefaultType(); } return null; diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/UnsignedType.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/UnsignedType.kt index 8ece3896068..e6e0360ec01 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/UnsignedType.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/UnsignedType.kt @@ -20,12 +20,27 @@ enum class UnsignedType(val classId: ClassId) { ULONG(ClassId.fromString("kotlin/ULong")); val typeName = classId.shortClassName - val arrayTypeName = Name.identifier(typeName.asString() + "Array") - val arrayClassId = ClassId(classId.packageFqName, arrayTypeName) + val arrayClassId = ClassId(classId.packageFqName, Name.identifier(typeName.asString() + "Array")) } object UnsignedTypes { - val unsignedTypeNames = enumValues().map { it.typeName }.toSet() + private val unsignedTypeNames = enumValues().map { it.typeName }.toSet() + private val arrayClassIdToUnsignedClassId = hashMapOf() + private val unsignedClassIdToArrayClassId = hashMapOf() + + private val arrayClassesShortNames: Set = UnsignedType.values().mapTo(mutableSetOf()) { it.arrayClassId.shortClassName } + + init { + for (unsignedType in UnsignedType.values()) { + arrayClassIdToUnsignedClassId[unsignedType.arrayClassId] = unsignedType.classId + unsignedClassIdToArrayClassId[unsignedType.classId] = unsignedType.arrayClassId + } + } + + fun isShortNameOfUnsignedArray(name: Name) = name in arrayClassesShortNames + + fun getUnsignedClassIdByArrayClassId(arrayClassId: ClassId): ClassId? = arrayClassIdToUnsignedClassId[arrayClassId] + fun getUnsignedArrayClassIdByUnsignedClassId(arrayClassId: ClassId): ClassId? = unsignedClassIdToArrayClassId[arrayClassId] fun isUnsignedType(type: KotlinType): Boolean { if (TypeUtils.noExpectedType(type)) return false @@ -40,4 +55,4 @@ object UnsignedTypes { container.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME && descriptor.name in UnsignedTypes.unsignedTypeNames } -} \ No newline at end of file +} diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index dbe64eedfe8..3e9af7a91a9 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -72,8 +72,8 @@ val ClassifierDescriptorWithTypeParameters.denotedClassDescriptor: ClassDescript else -> throw UnsupportedOperationException("Unexpected descriptor kind: $this") } -val ClassifierDescriptorWithTypeParameters.classId: ClassId? - get() = containingDeclaration.let { owner -> +val ClassifierDescriptor?.classId: ClassId? + get() = this?.containingDeclaration?.let { owner -> when (owner) { is PackageFragmentDescriptor -> ClassId(owner.fqName, name) is ClassifierDescriptorWithTypeParameters -> owner.classId?.createNestedClassId(name)