[Java Resolution, LL FIR] fix PsiInvalidElementAccessException from JavaClassifierTypeImpl.substitutor
The cached `PsiSubstitutor` might be invalidated, in this case we should recompute the value Also make the `JavaClassifierTypeImpl` to be thread safe ^KT-59667 fixed
This commit is contained in:
committed by
Space Team
parent
600b5ec6e5
commit
5675c19f3b
+61
-9
@@ -20,11 +20,13 @@ import com.intellij.psi.*
|
|||||||
import org.jetbrains.kotlin.load.java.structure.JavaClassifierType
|
import org.jetbrains.kotlin.load.java.structure.JavaClassifierType
|
||||||
import org.jetbrains.kotlin.load.java.structure.JavaType
|
import org.jetbrains.kotlin.load.java.structure.JavaType
|
||||||
import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementTypeSource
|
import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementTypeSource
|
||||||
|
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
|
||||||
|
|
||||||
class JavaClassifierTypeImpl(
|
class JavaClassifierTypeImpl(
|
||||||
psiClassTypeSource: JavaElementTypeSource<PsiClassType>,
|
psiClassTypeSource: JavaElementTypeSource<PsiClassType>,
|
||||||
) : JavaTypeImpl<PsiClassType>(psiClassTypeSource), JavaClassifierType {
|
) : JavaTypeImpl<PsiClassType>(psiClassTypeSource), JavaClassifierType {
|
||||||
|
|
||||||
|
@Volatile
|
||||||
private var resolutionResult: ResolutionResult? = null
|
private var resolutionResult: ResolutionResult? = null
|
||||||
|
|
||||||
override val classifier: JavaClassifierImpl<*>?
|
override val classifier: JavaClassifierImpl<*>?
|
||||||
@@ -63,21 +65,62 @@ class JavaClassifierTypeImpl(
|
|||||||
val classifier: JavaClassifierImpl<*>?,
|
val classifier: JavaClassifierImpl<*>?,
|
||||||
val substitutor: PsiSubstitutor,
|
val substitutor: PsiSubstitutor,
|
||||||
val isRaw: Boolean
|
val isRaw: Boolean
|
||||||
)
|
) {
|
||||||
|
/**
|
||||||
|
* Checks if the [ResolutionResult] is valid.
|
||||||
|
*
|
||||||
|
* The [PsiSubstitutor] which is contained inside [ResolutionResult] might become
|
||||||
|
* invalidated as it contains [PsiType]s inside
|
||||||
|
*
|
||||||
|
* @return true if the substitutor is valid, false otherwise.
|
||||||
|
*/
|
||||||
|
fun isValid(): Boolean {
|
||||||
|
return substitutor.isValid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the current [JavaClassifierType]
|
||||||
|
*
|
||||||
|
* The code is thread safe and the logic is the following:
|
||||||
|
* 1. Try to get a cached resolution result and return it if it's not invalidated
|
||||||
|
* 2. Otherwise, resolve the current [JavaClassifierType], update the cache and return the result.
|
||||||
|
*
|
||||||
|
* @returns [ResolutionResult] to which the [JavaClassifierType] resovled
|
||||||
|
*/
|
||||||
private fun resolve(): ResolutionResult {
|
private fun resolve(): ResolutionResult {
|
||||||
return resolutionResult ?: run {
|
while (true) {
|
||||||
val result = psi.resolveGenerics()
|
val snapshot = resolutionResult
|
||||||
val psiClass = result.element
|
@Suppress("LiftReturnOrAssignment")
|
||||||
val substitutor = result.substitutor
|
when {
|
||||||
ResolutionResult(
|
snapshot != null && snapshot.isValid() -> {
|
||||||
psiClass?.let { JavaClassifierImpl.create(it, sourceFactory) }, substitutor, PsiClassType.isRaw(result)
|
return snapshot
|
||||||
).apply {
|
}
|
||||||
resolutionResult = this
|
|
||||||
|
else -> {
|
||||||
|
val computedResult = computeResolveResult()
|
||||||
|
if (!resolutionResultAtomicFieldUpdater.compareAndSet(this, snapshot, computedResult)) {
|
||||||
|
// some other thread already computed the value,
|
||||||
|
// we should get it on the next `while` loop iteration.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return computedResult
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun computeResolveResult(): ResolutionResult {
|
||||||
|
val result = psi.resolveGenerics()
|
||||||
|
val psiClass = result.element
|
||||||
|
val substitutor = result.substitutor
|
||||||
|
return ResolutionResult(
|
||||||
|
psiClass?.let { JavaClassifierImpl.create(it, sourceFactory) },
|
||||||
|
substitutor,
|
||||||
|
PsiClassType.isRaw(result)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Copy-pasted from PsiUtil.typeParametersIterable
|
// Copy-pasted from PsiUtil.typeParametersIterable
|
||||||
// The only change is using `Collections.addAll(result, typeParameters)` instead of reversing type parameters of `currentOwner`
|
// The only change is using `Collections.addAll(result, typeParameters)` instead of reversing type parameters of `currentOwner`
|
||||||
// Result differs in cases like:
|
// Result differs in cases like:
|
||||||
@@ -103,4 +146,13 @@ class JavaClassifierTypeImpl(
|
|||||||
|
|
||||||
return result ?: emptyList()
|
return result ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
private val resolutionResultAtomicFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(
|
||||||
|
JavaClassifierTypeImpl::class.java,
|
||||||
|
ResolutionResult::class.java,
|
||||||
|
JavaClassifierTypeImpl::resolutionResult.name
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user