mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
[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:
committed by
intellij-monorepo-bot
parent
c3f31ae2df
commit
e15d9f8ae9
@@ -276,111 +276,132 @@ 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 (element is UCallExpression) {
|
if (curElement is UDeclaration && declarationEvaluator != null) {
|
||||||
val methodEvaluator = builderEvaluator.methodDescriptions.entries.firstOrNull { (pattern, _) ->
|
val value = declarationEvaluator?.invoke(curElement)
|
||||||
pattern.accepts(element.resolve())
|
if (value != null) {
|
||||||
|
result = value
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (methodEvaluator != null) {
|
|
||||||
val dependencies = graph.dependencies[element].orEmpty()
|
if (curElement is UCallExpression) {
|
||||||
val isStrict = objectsToAnalyze == originalObjectsToAnalyze
|
val methodEvaluator = builderEvaluator.methodDescriptions.entries.firstOrNull { (pattern, _) ->
|
||||||
return when (
|
pattern.accepts(curElement.resolve())
|
||||||
val dependency = dependencies
|
}
|
||||||
.firstOrNull { it !is Dependency.PotentialSideEffectDependency && it !is Dependency.ArgumentDependency }
|
if (methodEvaluator != null) {
|
||||||
) {
|
val dependencies = graph.dependencies[curElement].orEmpty()
|
||||||
is Dependency.BranchingDependency -> {
|
val isStrict = objectsToAnalyze == originalObjectsToAnalyze
|
||||||
val branchResult = dependency.elements.mapNotNull {
|
when (
|
||||||
calculateBuilder(it, objectsToAnalyze, originalObjectsToAnalyze)
|
val dependency = dependencies
|
||||||
}.let { strategy.constructValueFromList(element, it) }
|
.firstOrNull { it !is Dependency.PotentialSideEffectDependency && it !is Dependency.ArgumentDependency }
|
||||||
methodEvaluator.value.evaluate(element, branchResult, this@UNeDfaValueEvaluator, configuration, isStrict)
|
) {
|
||||||
|
is Dependency.BranchingDependency -> {
|
||||||
|
val branchResult = dependency.elements.mapNotNull {
|
||||||
|
calculateBuilder(it, objectsToAnalyze, originalObjectsToAnalyze)
|
||||||
|
}.let { strategy.constructValueFromList(curElement, it) }
|
||||||
|
result = methodEvaluator.value.evaluate(curElement, branchResult, this@UNeDfaValueEvaluator, configuration, isStrict)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
is Dependency.CommonDependency -> {
|
||||||
|
nextElement = dependency.element
|
||||||
|
converters += { methodEvaluator.value.evaluate(curElement, it, this@UNeDfaValueEvaluator, configuration, isStrict) }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
is Dependency.PotentialSideEffectDependency -> {
|
||||||
|
result = null // there should not be anything
|
||||||
|
break
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
result = methodEvaluator.value.evaluate(curElement, null, this@UNeDfaValueEvaluator, configuration, isStrict)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is Dependency.CommonDependency -> {
|
}
|
||||||
val result = calculateBuilder(
|
}
|
||||||
dependency.element,
|
|
||||||
objectsToAnalyze,
|
val dependencies = graph.dependencies[curElement].orEmpty()
|
||||||
originalObjectsToAnalyze
|
|
||||||
)
|
if (dependencies.isEmpty() && curElement is UReferenceExpression) {
|
||||||
methodEvaluator.value.evaluate(element, result, this@UNeDfaValueEvaluator, configuration, isStrict)
|
val declaration = curElement.resolveToUElement()
|
||||||
|
if (declaration is UParameter && declaration.uastParent is UMethod && declaration.uastParent == graph.uAnchor) {
|
||||||
|
val usagesResults = analyzeUsages(declaration.uastParent as UMethod, declaration,
|
||||||
|
configuration) { usageGraph, argument, usageConfiguration ->
|
||||||
|
BuilderEvaluator(usageGraph, usageConfiguration, builderEvaluator).calculateBuilder(argument, null, null)
|
||||||
}
|
}
|
||||||
is Dependency.PotentialSideEffectDependency -> null // there should not be anything
|
result = usagesResults.singleOrNull() ?: strategy.constructValueFromList(curElement, usagesResults)
|
||||||
else -> methodEvaluator.value.evaluate(element, null, this@UNeDfaValueEvaluator, configuration, isStrict)
|
break
|
||||||
|
}
|
||||||
|
if (declaration is UField && configuration.isAppropriateField(declaration)) {
|
||||||
|
val declarationResult = listOfNotNull(UastLocalUsageDependencyGraph.getGraphByUElement(declaration)?.let { graphForField ->
|
||||||
|
declaration.uastInitializer?.let { initializer ->
|
||||||
|
BuilderEvaluator(graphForField, configuration, builderEvaluator).calculateBuilder(initializer, null, null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
result = declarationResult.singleOrNull() ?: strategy.constructUnknownValue(curElement)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val (dependency, candidates) = selectDependency(dependencies, builderEvaluator) {
|
||||||
|
(originalObjectsToAnalyze == null ||
|
||||||
|
originalObjectsToAnalyze.intersect(it.dependencyWitnessValues).isNotEmpty()) &&
|
||||||
|
provePossibleDependency(it.dependencyEvidence, builderEvaluator)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
dependency is DependencyOfReference &&
|
||||||
|
originalObjectsToAnalyze != null &&
|
||||||
|
dependency.referenceInfo?.possibleReferencedValues?.intersect(originalObjectsToAnalyze)?.isEmpty() == true
|
||||||
|
) {
|
||||||
|
result = null
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
when (dependency) {
|
||||||
|
is Dependency.BranchingDependency -> {
|
||||||
|
val variants = dependency.elements.mapNotNull {
|
||||||
|
calculateBuilder(it, objectsToAnalyze, originalObjectsToAnalyze)
|
||||||
|
}.takeUnless { it.isEmpty() }
|
||||||
|
|
||||||
|
result = if (variants?.size == 1) {
|
||||||
|
variants.single()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
strategy.constructValueFromList(curElement, variants)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
is Dependency.CommonDependency -> {
|
||||||
|
nextElement = dependency.element
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
is Dependency.PotentialSideEffectDependency -> {
|
||||||
|
result = if (!builderEvaluator.allowSideEffects) null
|
||||||
|
else {
|
||||||
|
candidates
|
||||||
|
.mapNotNull { candidate ->
|
||||||
|
calculateBuilder(
|
||||||
|
candidate.updateElement,
|
||||||
|
candidate.dependencyWitnessValues,
|
||||||
|
originalObjectsToAnalyze ?: dependency.referenceInfo?.possibleReferencedValues
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.let { strategy.constructValueFromList(curElement, it) }
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
result = null
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return converters.asReversed().fold(result) { acc, converter -> converter(acc) }
|
||||||
val dependencies = graph.dependencies[element].orEmpty()
|
|
||||||
|
|
||||||
if (dependencies.isEmpty() && element is UReferenceExpression) {
|
|
||||||
val declaration = element.resolveToUElement()
|
|
||||||
if (declaration is UParameter && declaration.uastParent is UMethod && declaration.uastParent == graph.uAnchor) {
|
|
||||||
val usagesResults = analyzeUsages(declaration.uastParent as UMethod, declaration,
|
|
||||||
configuration) { usageGraph, argument, usageConfiguration ->
|
|
||||||
BuilderEvaluator(usageGraph, usageConfiguration, builderEvaluator).calculateBuilder(argument, null, null)
|
|
||||||
}
|
|
||||||
return usagesResults.singleOrNull() ?: strategy.constructValueFromList(element, usagesResults)
|
|
||||||
}
|
|
||||||
if (declaration is UField && configuration.isAppropriateField(declaration)) {
|
|
||||||
val declarationResult = listOfNotNull(UastLocalUsageDependencyGraph.getGraphByUElement(declaration)?.let { graphForField ->
|
|
||||||
declaration.uastInitializer?.let { initializer ->
|
|
||||||
BuilderEvaluator(graphForField, configuration, builderEvaluator).calculateBuilder(initializer, null, null)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return declarationResult.singleOrNull() ?: strategy.constructUnknownValue(element)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val (dependency, candidates) = selectDependency(dependencies, builderEvaluator) {
|
|
||||||
(originalObjectsToAnalyze == null ||
|
|
||||||
originalObjectsToAnalyze.intersect(it.dependencyWitnessValues).isNotEmpty()) &&
|
|
||||||
provePossibleDependency(it.dependencyEvidence, builderEvaluator)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
dependency is DependencyOfReference &&
|
|
||||||
originalObjectsToAnalyze != null &&
|
|
||||||
dependency.referenceInfo?.possibleReferencedValues?.intersect(originalObjectsToAnalyze)?.isEmpty() == true
|
|
||||||
) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return when (dependency) {
|
|
||||||
is Dependency.BranchingDependency -> {
|
|
||||||
val variants = dependency.elements.mapNotNull {
|
|
||||||
calculateBuilder(it, objectsToAnalyze, originalObjectsToAnalyze)
|
|
||||||
}.takeUnless { it.isEmpty() }
|
|
||||||
|
|
||||||
if (variants?.size == 1) {
|
|
||||||
variants.single()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
strategy.constructValueFromList(element, variants)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is Dependency.CommonDependency -> {
|
|
||||||
calculateBuilder(
|
|
||||||
dependency.element,
|
|
||||||
objectsToAnalyze,
|
|
||||||
originalObjectsToAnalyze
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is Dependency.PotentialSideEffectDependency -> if (!builderEvaluator.allowSideEffects) null
|
|
||||||
else {
|
|
||||||
candidates
|
|
||||||
.mapNotNull { candidate ->
|
|
||||||
calculateBuilder(
|
|
||||||
candidate.updateElement,
|
|
||||||
candidate.dependencyWitnessValues,
|
|
||||||
originalObjectsToAnalyze ?: dependency.referenceInfo?.possibleReferencedValues
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.let { strategy.constructValueFromList(element, it) }
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user