[uast] BuilderEvaluator.calculateBuilder reduce stack consumption

While not completely non-recursive, the updated implementation eats 3x less stack frames in `UStringEvaluatorWithSideEffectsTest.test many appends` topping at 328 frames instead of 1024 before
Fixes IDEA-345197 Test org.jetbrains.uast.test.java.analysis.UStringEvaluatorWithSideEffectsTest.test many appends failed on jbr17

GitOrigin-RevId: aeaee268a8e2e8d9a2c12709096215059f9694e4
This commit is contained in:
Tagir Valeev
2024-07-12 17:33:22 +02:00
committed by intellij-monorepo-bot
parent c3f31ae2df
commit e15d9f8ae9

View File

@@ -276,52 +276,65 @@ class UNeDfaValueEvaluator<T : Any>(private val strategy: UValueEvaluatorStrateg
objectsToAnalyze: Collection<UValueMark>?, objectsToAnalyze: Collection<UValueMark>?,
originalObjectsToAnalyze: Collection<UValueMark>? originalObjectsToAnalyze: Collection<UValueMark>?
): T? { ): T? {
if (element is UDeclaration && declarationEvaluator != null) { var nextElement = element
val value = declarationEvaluator?.invoke(element) val converters = mutableListOf<(T?) -> T?>()
if (value != null) return value val result: T?
while (true) {
val curElement = nextElement
if (curElement is UDeclaration && declarationEvaluator != null) {
val value = declarationEvaluator?.invoke(curElement)
if (value != null) {
result = value
break
}
} }
if (element is UCallExpression) { if (curElement is UCallExpression) {
val methodEvaluator = builderEvaluator.methodDescriptions.entries.firstOrNull { (pattern, _) -> val methodEvaluator = builderEvaluator.methodDescriptions.entries.firstOrNull { (pattern, _) ->
pattern.accepts(element.resolve()) pattern.accepts(curElement.resolve())
} }
if (methodEvaluator != null) { if (methodEvaluator != null) {
val dependencies = graph.dependencies[element].orEmpty() val dependencies = graph.dependencies[curElement].orEmpty()
val isStrict = objectsToAnalyze == originalObjectsToAnalyze val isStrict = objectsToAnalyze == originalObjectsToAnalyze
return when ( when (
val dependency = dependencies val dependency = dependencies
.firstOrNull { it !is Dependency.PotentialSideEffectDependency && it !is Dependency.ArgumentDependency } .firstOrNull { it !is Dependency.PotentialSideEffectDependency && it !is Dependency.ArgumentDependency }
) { ) {
is Dependency.BranchingDependency -> { is Dependency.BranchingDependency -> {
val branchResult = dependency.elements.mapNotNull { val branchResult = dependency.elements.mapNotNull {
calculateBuilder(it, objectsToAnalyze, originalObjectsToAnalyze) calculateBuilder(it, objectsToAnalyze, originalObjectsToAnalyze)
}.let { strategy.constructValueFromList(element, it) } }.let { strategy.constructValueFromList(curElement, it) }
methodEvaluator.value.evaluate(element, branchResult, this@UNeDfaValueEvaluator, configuration, isStrict) result = methodEvaluator.value.evaluate(curElement, branchResult, this@UNeDfaValueEvaluator, configuration, isStrict)
break
} }
is Dependency.CommonDependency -> { is Dependency.CommonDependency -> {
val result = calculateBuilder( nextElement = dependency.element
dependency.element, converters += { methodEvaluator.value.evaluate(curElement, it, this@UNeDfaValueEvaluator, configuration, isStrict) }
objectsToAnalyze, continue
originalObjectsToAnalyze }
) is Dependency.PotentialSideEffectDependency -> {
methodEvaluator.value.evaluate(element, result, this@UNeDfaValueEvaluator, configuration, isStrict) result = null // there should not be anything
break
}
else -> {
result = methodEvaluator.value.evaluate(curElement, null, this@UNeDfaValueEvaluator, configuration, isStrict)
break
} }
is Dependency.PotentialSideEffectDependency -> null // there should not be anything
else -> methodEvaluator.value.evaluate(element, null, this@UNeDfaValueEvaluator, configuration, isStrict)
} }
} }
} }
val dependencies = graph.dependencies[element].orEmpty() val dependencies = graph.dependencies[curElement].orEmpty()
if (dependencies.isEmpty() && element is UReferenceExpression) { if (dependencies.isEmpty() && curElement is UReferenceExpression) {
val declaration = element.resolveToUElement() val declaration = curElement.resolveToUElement()
if (declaration is UParameter && declaration.uastParent is UMethod && declaration.uastParent == graph.uAnchor) { if (declaration is UParameter && declaration.uastParent is UMethod && declaration.uastParent == graph.uAnchor) {
val usagesResults = analyzeUsages(declaration.uastParent as UMethod, declaration, val usagesResults = analyzeUsages(declaration.uastParent as UMethod, declaration,
configuration) { usageGraph, argument, usageConfiguration -> configuration) { usageGraph, argument, usageConfiguration ->
BuilderEvaluator(usageGraph, usageConfiguration, builderEvaluator).calculateBuilder(argument, null, null) BuilderEvaluator(usageGraph, usageConfiguration, builderEvaluator).calculateBuilder(argument, null, null)
} }
return usagesResults.singleOrNull() ?: strategy.constructValueFromList(element, usagesResults) result = usagesResults.singleOrNull() ?: strategy.constructValueFromList(curElement, usagesResults)
break
} }
if (declaration is UField && configuration.isAppropriateField(declaration)) { if (declaration is UField && configuration.isAppropriateField(declaration)) {
val declarationResult = listOfNotNull(UastLocalUsageDependencyGraph.getGraphByUElement(declaration)?.let { graphForField -> val declarationResult = listOfNotNull(UastLocalUsageDependencyGraph.getGraphByUElement(declaration)?.let { graphForField ->
@@ -329,7 +342,8 @@ class UNeDfaValueEvaluator<T : Any>(private val strategy: UValueEvaluatorStrateg
BuilderEvaluator(graphForField, configuration, builderEvaluator).calculateBuilder(initializer, null, null) BuilderEvaluator(graphForField, configuration, builderEvaluator).calculateBuilder(initializer, null, null)
} }
}) })
return declarationResult.singleOrNull() ?: strategy.constructUnknownValue(element) result = declarationResult.singleOrNull() ?: strategy.constructUnknownValue(curElement)
break
} }
} }
@@ -344,30 +358,30 @@ class UNeDfaValueEvaluator<T : Any>(private val strategy: UValueEvaluatorStrateg
originalObjectsToAnalyze != null && originalObjectsToAnalyze != null &&
dependency.referenceInfo?.possibleReferencedValues?.intersect(originalObjectsToAnalyze)?.isEmpty() == true dependency.referenceInfo?.possibleReferencedValues?.intersect(originalObjectsToAnalyze)?.isEmpty() == true
) { ) {
return null result = null
break
} }
return when (dependency) { when (dependency) {
is Dependency.BranchingDependency -> { is Dependency.BranchingDependency -> {
val variants = dependency.elements.mapNotNull { val variants = dependency.elements.mapNotNull {
calculateBuilder(it, objectsToAnalyze, originalObjectsToAnalyze) calculateBuilder(it, objectsToAnalyze, originalObjectsToAnalyze)
}.takeUnless { it.isEmpty() } }.takeUnless { it.isEmpty() }
if (variants?.size == 1) { result = if (variants?.size == 1) {
variants.single() variants.single()
} }
else { else {
strategy.constructValueFromList(element, variants) strategy.constructValueFromList(curElement, variants)
} }
break
} }
is Dependency.CommonDependency -> { is Dependency.CommonDependency -> {
calculateBuilder( nextElement = dependency.element
dependency.element, continue
objectsToAnalyze,
originalObjectsToAnalyze
)
} }
is Dependency.PotentialSideEffectDependency -> if (!builderEvaluator.allowSideEffects) null is Dependency.PotentialSideEffectDependency -> {
result = if (!builderEvaluator.allowSideEffects) null
else { else {
candidates candidates
.mapNotNull { candidate -> .mapNotNull { candidate ->
@@ -377,10 +391,17 @@ class UNeDfaValueEvaluator<T : Any>(private val strategy: UValueEvaluatorStrateg
originalObjectsToAnalyze ?: dependency.referenceInfo?.possibleReferencedValues originalObjectsToAnalyze ?: dependency.referenceInfo?.possibleReferencedValues
) )
} }
.let { strategy.constructValueFromList(element, it) } .let { strategy.constructValueFromList(curElement, it) }
} }
else -> null break
} }
else -> {
result = null
break
}
}
}
return converters.asReversed().fold(result) { acc, converter -> converter(acc) }
} }
} }