JS: when recursive local function does not capture variables, give it name from top-level scope. See KT-15770

This commit is contained in:
Alexey Andreev
2017-01-18 12:43:28 +03:00
committed by Alexey Andreev
parent 70d2296e61
commit ac0df8eef9
3 changed files with 60 additions and 13 deletions
@@ -5678,6 +5678,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
doTest(fileName);
}
@TestMetadata("localFunctions.kt")
public void testLocalFunctions() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/localFunctions.kt");
doTest(fileName);
}
@TestMetadata("methodAndPrivateProperty.kt")
public void testMethodAndPrivateProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/nameClashes/methodAndPrivateProperty.kt");
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.js.translate.expression
import org.jetbrains.kotlin.backend.common.SUSPENDED_MARKER_NAME
import org.jetbrains.kotlin.builtins.isBuiltinExtensionFunctionalType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
@@ -24,10 +23,10 @@ import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.*
import org.jetbrains.kotlin.js.descriptorUtils.isCoroutineLambda
import org.jetbrains.kotlin.js.inline.util.getInnerFunction
import org.jetbrains.kotlin.js.inline.util.rewriters.NameReplacingVisitor
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.context.getNameForCapturedDescriptor
import org.jetbrains.kotlin.js.translate.context.hasCapturedExceptContaining
import org.jetbrains.kotlin.js.translate.context.isCaptured
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getFunctionDescriptor
import org.jetbrains.kotlin.js.translate.utils.FunctionBodyTranslator.setDefaultValueForArguments
@@ -73,21 +72,12 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
val tracker = functionContext.usageTracker()!!
val isRecursive = tracker.isCaptured(descriptor)
lambda.name = if (isRecursive) {
tracker.getNameForCapturedDescriptor(descriptor)
}
else {
invokingContext.getInnerNameForDescriptor(descriptor)
}
if (tracker.hasCapturedExceptContaining()) {
val lambdaCreator = simpleReturnFunction(invokingContext.scope(), lambda)
lambdaCreator.name = invokingContext.getInnerNameForDescriptor(descriptor)
lambdaCreator.isLocal = true
if (!isRecursive || descriptor.isCoroutineLambda) {
lambda.name = null
if (descriptor in tracker.capturedDescriptors && !descriptor.isCoroutineLambda) {
lambda.name = tracker.getNameForCapturedDescriptor(descriptor)
}
lambdaCreator.name.staticRef = lambdaCreator
lambdaCreator.fillCoroutineMetadata(invokingContext, descriptor, continuationType)
@@ -95,6 +85,14 @@ class LiteralFunctionTranslator(context: TranslationContext) : AbstractTranslato
invokingContext)
}
lambda.name = invokingContext.getInnerNameForDescriptor(descriptor)
if (descriptor in tracker.capturedDescriptors) {
val capturedName = tracker.getNameForCapturedDescriptor(descriptor)!!
val globalName = invokingContext.getInnerNameForDescriptor(descriptor)
val replacingVisitor = NameReplacingVisitor(mapOf(capturedName to JsAstUtils.pureFqn(globalName, null)))
replacingVisitor.accept(lambda)
}
lambda.isLocal = true
invokingContext.addDeclarationStatement(lambda.makeStmt())
@@ -0,0 +1,43 @@
var log = ""
fun foo() {
fun local(x: String) {
if (x.isEmpty()) return
log += "foo(${x[0]});"
local(x.substring(1))
}
local("OK")
}
fun bar() {
fun local(x: String) {
if (x.isEmpty()) return
log += "bar(${x[0]});"
local(x.substring(1))
}
local("OK")
}
fun baz(): Int {
fun local(x: String) {
if (x.isEmpty()) return
log += "baz(${x[0]});"
local(x.substring(1))
}
fun local(x: Int): Int = if (x == 0) 1 else x * local(x - 1)
local("OK")
return local(3)
}
fun box(): String {
foo()
bar()
val result = baz()
if (result != 6) return "fail1: $result"
if (log != "foo(O);foo(K);bar(O);bar(K);baz(O);baz(K);") return "fail2: $result"
return "OK"
}