Avoid exponential complexity in LexicalScope::findLocalVariable

There was an exponential complexity in case we have a chain
of nested LexicalScopeWrapper instances:
we were walking through the chain and on each step running
the algorithm recursively.

Such a case is possible in scripts where each line contains LexicalScopeWrapper

The fix looks safe to me because delegate was used there
for checking !is ImportingScope && !is LexicalChainedScope

In all other means, being recursively run the algorithm does the same job.

 #KT-22740 Fixed
This commit is contained in:
Denis Zharkov
2018-04-10 15:35:04 +03:00
parent a3c8afade5
commit 1442dd6c2b
2 changed files with 25 additions and 7 deletions
@@ -64,14 +64,19 @@ fun HierarchicalScope.collectDescriptorsFiltered(
@Deprecated("Use getContributedProperties instead")
fun LexicalScope.findLocalVariable(name: Name): VariableDescriptor? {
return findFirstFromMeAndParent {
when {
it is LexicalScopeWrapper -> it.delegate.findLocalVariable(name)
return findFirstFromMeAndParent { originalScope ->
// Unpacking LexicalScopeWrapper may be important to check that it is not ImportingScope
val possiblyUnpackedScope = when (originalScope) {
is LexicalScopeWrapper -> originalScope.delegate
else -> originalScope
}
it !is ImportingScope && it !is LexicalChainedScope -> it.getContributedVariables(
name,
NoLookupLocation.WHEN_GET_LOCAL_VARIABLE
).singleOrNull() /* todo check this*/
when {
possiblyUnpackedScope !is ImportingScope && possiblyUnpackedScope !is LexicalChainedScope ->
possiblyUnpackedScope.getContributedVariables(
name,
NoLookupLocation.WHEN_GET_LOCAL_VARIABLE
).singleOrNull() /* todo check this*/
else -> null
}
@@ -157,6 +157,19 @@ class GenericReplTest : KtUsefulTestCase() {
assertEquals(res.second.toString(), evals, (res.second as? ReplEvalResult.ValueResult)?.value)
}
}
fun testReplSlowdownKt22740() {
TestRepl().use { repl ->
val state = repl.createState()
repl.compileAndEval(state, ReplCodeLine(0, 0, "class Test<T>(val x: T) { fun <R> map(f: (T) -> R): R = f(x) }".trimIndent()))
// We expect that analysis time is not exponential
for (i in 1..60) {
repl.compileAndEval(state, ReplCodeLine(i, 0, "fun <T> Test<T>.map(f: (T) -> Double): List<Double> = listOf(f(this.x))"))
}
}
}
}