From 1442dd6c2bd50ab6e57d57d26671d617421a5729 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 10 Apr 2018 15:35:04 +0300 Subject: [PATCH] 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 --- .../kotlin/resolve/scopes/utils/ScopeUtils.kt | 19 ++++++++++++------- .../kotlin/cli/jvm/repl/GenericReplTest.kt | 13 +++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt index da2eca6fa8f..84f995a6ce5 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt @@ -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 } diff --git a/compiler/tests/org/jetbrains/kotlin/cli/jvm/repl/GenericReplTest.kt b/compiler/tests/org/jetbrains/kotlin/cli/jvm/repl/GenericReplTest.kt index f6636012fd4..5042b54aa48 100644 --- a/compiler/tests/org/jetbrains/kotlin/cli/jvm/repl/GenericReplTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/cli/jvm/repl/GenericReplTest.kt @@ -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(val x: T) { fun 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 Test.map(f: (T) -> Double): List = listOf(f(this.x))")) + } + } + } }