From 4f1e85b46895d3799f6e361e0433d037215a15ff Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 31 May 2019 14:25:13 +0300 Subject: [PATCH] [Misc] Add caching of hashCode in KotlinType KT-32852 --- .../org/jetbrains/kotlin/types/KotlinType.kt | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/KotlinType.kt b/core/descriptors/src/org/jetbrains/kotlin/types/KotlinType.kt index e85a28d6690..489ed6bbd89 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/KotlinType.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/KotlinType.kt @@ -53,7 +53,19 @@ sealed class KotlinType : Annotated, KotlinTypeMarker { abstract fun unwrap(): UnwrappedType - final override fun hashCode(): Int { + /* '0' means "hashCode wasn't computed" + + Note #1. We don't use 'null' as a sign of "uncomputed value" to avoid boxing, + and even if we get that rumored "integer hashCode collision", we'd just lose + caching for that "unlucky" instance + + Note #2. We don't use @Volatile even though that field can be accessed concurrently. + The reason is that contended volatile reads may be harmful for performance, + and there's no harm in computing this value several times concurrently + */ + private var cachedHashCode: Int = 0 + + private fun computeHashCode(): Int { if (isError) return super.hashCode() var result = constructor.hashCode() @@ -62,6 +74,18 @@ sealed class KotlinType : Annotated, KotlinTypeMarker { return result } + final override fun hashCode(): Int { + // NB: make one read to prevent race + var currentHashCode = cachedHashCode + if (currentHashCode == 0) return currentHashCode + + currentHashCode = computeHashCode() + + cachedHashCode = currentHashCode + + return currentHashCode + } + final override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is KotlinType) return false