From 38575cdbc145aeab2c07b7c61a25f70368866d03 Mon Sep 17 00:00:00 2001 From: Dmitrii Gridin Date: Tue, 31 Jan 2023 14:23:57 +0100 Subject: [PATCH] [FIR] FirBasedSymbol: iterate annotations by index instead of iterator to avoid possible ConcurrentModificationException ^KT-56046 --- .../api/fir/annotations/firAnnotationUtils.kt | 29 +++++++++++++------ .../kotlin/fir/symbols/FirBasedSymbol.kt | 12 +++++++- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt index af4977b0eaa..04d656ce1dd 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/annotations/firAnnotationUtils.kt @@ -51,12 +51,15 @@ internal fun annotationsByClassId( annotationContainer: FirAnnotationContainer = firSymbol.fir, ): List = if (firSymbol.isFromCompilerRequiredAnnotationsPhase(classId)) { - annotationContainer.resolvedCompilerRequiredAnnotations(firSymbol).mapIndexedNotNull { index, annotation -> - if (!useSiteTargetFilter.isAllowed(annotation.useSiteTarget) || annotation.toAnnotationClassIdSafe(useSiteSession) != classId) { - return@mapIndexedNotNull null + buildList { + // this loop by index is required to avoid possible ConcurrentModificationException + val annotations = annotationContainer.resolvedCompilerRequiredAnnotations(firSymbol) + for (index in annotations.indices) { + val annotation = annotations[index] + if (useSiteTargetFilter.isAllowed(annotation.useSiteTarget) && annotation.toAnnotationClassIdSafe(useSiteSession) == classId) { + add(annotation.toKtAnnotationApplication(useSiteSession, index)) + } } - - annotation.toKtAnnotationApplication(useSiteSession, index) } } else { annotationContainer.resolvedAnnotationsWithArguments(firSymbol).mapIndexedNotNull { index, annotation -> @@ -99,16 +102,24 @@ internal fun hasAnnotation( useSiteTargetFilter: AnnotationUseSiteTargetFilter, useSiteSession: FirSession, annotationContainer: FirAnnotationContainer = firSymbol.fir, -): Boolean = - if (firSymbol.isFromCompilerRequiredAnnotationsPhase(classId)) { - annotationContainer.resolvedCompilerRequiredAnnotations(firSymbol).any { - useSiteTargetFilter.isAllowed(it.useSiteTarget) && it.toAnnotationClassIdSafe(useSiteSession) == classId +): Boolean { + return if (firSymbol.isFromCompilerRequiredAnnotationsPhase(classId)) { + // this loop by index is required to avoid possible ConcurrentModificationException + val annotations = annotationContainer.resolvedCompilerRequiredAnnotations(firSymbol) + for (index in annotations.indices) { + val annotation = annotations[index] + if (useSiteTargetFilter.isAllowed(annotation.useSiteTarget) && annotation.toAnnotationClassIdSafe(useSiteSession) == classId) { + return true + } } + + false } else { annotationContainer.resolvedAnnotationsWithClassIds(firSymbol).any { useSiteTargetFilter.isAllowed(it.useSiteTarget) && it.toAnnotationClassId(useSiteSession) == classId } } +} private fun FirBasedSymbol<*>.isFromCompilerRequiredAnnotationsPhase(classId: ClassId): Boolean = fir.resolvePhase < FirResolvePhase.TYPES && classId in CompilerRequiredAnnotationsHelper.REQUIRED_ANNOTATIONS \ No newline at end of file diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/FirBasedSymbol.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/FirBasedSymbol.kt index 5c978618d32..08c2d46b248 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/FirBasedSymbol.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/FirBasedSymbol.kt @@ -79,7 +79,17 @@ fun FirAnnotationContainer.resolvedAnnotationsWithArguments(anchorElement: FirBa fun List.resolvedAnnotationsWithArguments(anchorElement: FirBasedSymbol<*>): List { if (isEmpty()) return emptyList() - val phase = if (any { it is FirAnnotationCall && it.arguments.isNotEmpty() }) { + // this loop by index is required to avoid possible ConcurrentModificationException + var hasAnnotationCallWithArguments = false + for (i in indices) { + val currentAnnotation = get(i) + if (currentAnnotation is FirAnnotationCall && currentAnnotation.arguments.isNotEmpty()) { + hasAnnotationCallWithArguments = true + break + } + } + + val phase = if (hasAnnotationCallWithArguments) { FirResolvePhase.ANNOTATIONS_ARGUMENTS_MAPPING } else { FirResolvePhase.TYPES