diff --git a/compiler/testData/codegen/box/localClasses/ownClosureOfInnerLocalClass.kt b/compiler/testData/codegen/box/localClasses/ownClosureOfInnerLocalClass.kt index 0980e7df7fa..c978f8b9c62 100644 --- a/compiler/testData/codegen/box/localClasses/ownClosureOfInnerLocalClass.kt +++ b/compiler/testData/codegen/box/localClasses/ownClosureOfInnerLocalClass.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND: JS -// Enable when KT-12566 gets fixed - fun box(): String { var log = "" diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index c95e1040e55..d3520f2be3d 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -11100,13 +11100,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { @TestMetadata("ownClosureOfInnerLocalClass.kt") public void testOwnClosureOfInnerLocalClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/localClasses/ownClosureOfInnerLocalClass.kt"); - try { - doTest(fileName); - } - catch (Throwable ignore) { - return; - } - throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + doTest(fileName); } @TestMetadata("withclosure.kt") diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index 6a5fa0003ef..2dcbefd803a 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -166,6 +166,11 @@ public class TranslationContext { return new TranslationContext(this, staticContext, dynamicContext, aliasingContext.inner(), usageTracker, descriptor); } + @NotNull + public TranslationContext inner(@NotNull MemberDescriptor descriptor) { + return new TranslationContext(this, staticContext, dynamicContext, aliasingContext.inner(), usageTracker, descriptor); + } + @NotNull public TranslationContext innerBlock(@NotNull JsBlock block) { return new TranslationContext(this, staticContext, dynamicContext.innerBlock(block), aliasingContext, usageTracker, @@ -379,7 +384,7 @@ public class TranslationContext { @Nullable public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) { - JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor); + JsExpression nameRef = captureIfNeedAndGetCapturedName(descriptor); if (nameRef != null) { return nameRef; } @@ -459,15 +464,23 @@ public class TranslationContext { } @Nullable - private JsNameRef captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) { + private JsExpression captureIfNeedAndGetCapturedName(DeclarationDescriptor descriptor) { if (usageTracker != null) { usageTracker.used(descriptor); JsName name = getNameForCapturedDescriptor(usageTracker, descriptor); if (name != null) { - JsNameRef result = name.makeRef(); + JsExpression result; if (shouldCaptureViaThis()) { - result.setQualifier(JsLiteral.THIS); + result = JsLiteral.THIS; + int depth = getOuterLocalClassDepth(); + for (int i = 0; i < depth; ++i) { + result = new JsNameRef(Namer.OUTER_FIELD_NAME, result); + } + result = new JsNameRef(name, result); + } + else { + result = name.makeRef(); } return result; } @@ -476,6 +489,25 @@ public class TranslationContext { return null; } + private int getOuterLocalClassDepth() { + if (usageTracker == null) return 0; + MemberDescriptor capturingDescriptor = usageTracker.getContainingDescriptor(); + if (!(capturingDescriptor instanceof ClassDescriptor)) return 0; + + ClassDescriptor capturingClassDescriptor = (ClassDescriptor) capturingDescriptor; + ClassDescriptor currentDescriptor = classDescriptor; + if (currentDescriptor == null) return 0; + + int depth = 0; + while (currentDescriptor != capturingClassDescriptor) { + DeclarationDescriptor container = currentDescriptor.getContainingDeclaration(); + if (!(container instanceof ClassDescriptor)) return 0; + currentDescriptor = (ClassDescriptor) container; + depth++; + } + return depth; + } + private boolean shouldCaptureViaThis() { if (declarationDescriptor == null) return false; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt index e6c21b60e8c..e11fe0bbb24 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/declaration/ClassTranslator.kt @@ -79,7 +79,7 @@ class ClassTranslator private constructor( context.addDeclarationStatement(constructorFunction.makeStmt()) val enumInitFunction = if (descriptor.kind == ClassKind.ENUM_CLASS) createEnumInitFunction() else null - val nonConstructorContext = context.innerWithUsageTracker(scope, descriptor) + val nonConstructorContext = context.withUsageTrackerIfNecessary(scope, descriptor) nonConstructorContext.startDeclaration() val delegationTranslator = DelegationTranslator(classDeclaration, nonConstructorContext) translatePropertiesAsConstructorParameters(nonConstructorContext) @@ -93,7 +93,7 @@ class ClassTranslator private constructor( addMetadataType() context.addClass(descriptor) addSuperclassReferences() - classDeclaration.getSecondaryConstructors().forEach { generateSecondaryConstructor(context, it) } + classDeclaration.secondaryConstructors.forEach { generateSecondaryConstructor(context, it) } generatedBridgeMethods() @@ -115,6 +115,18 @@ class ClassTranslator private constructor( } } + private fun TranslationContext.withUsageTrackerIfNecessary(scope: JsScope, innerDescriptor: MemberDescriptor): TranslationContext { + return if (isLocalClass) { + innerWithUsageTracker(scope, innerDescriptor) + } + else { + inner(innerDescriptor) + } + } + + private val isLocalClass + get() = descriptor.containingDeclaration !is ClassOrPackageFragmentDescriptor + private fun translatePrimaryConstructor( constructorFunction: JsFunction, classContext: TranslationContext, @@ -303,10 +315,10 @@ class ClassTranslator private constructor( for (constructor in sortedConstructors) { constructor.superCallGenerator() - val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!! + val nonConstructorUsageTracker = nonConstructorContext.usageTracker() val usageTracker = constructor.context.usageTracker()!! - val nonConstructorCapturedVars = nonConstructorUsageTracker.capturedDescriptors + val nonConstructorCapturedVars = if (isLocalClass) nonConstructorUsageTracker!!.capturedDescriptors else setOf() val constructorCapturedVars = usageTracker.capturedDescriptors val capturedVars = (nonConstructorCapturedVars + constructorCapturedVars).distinct() @@ -319,9 +331,9 @@ class ClassTranslator private constructor( for (callSite in constructorCallSites) { val closureQualifier = callSite.context.getArgumentForClosureConstructor(classDescriptor.thisAsReceiverParameter) - capturedVars.forEach { nonConstructorUsageTracker.used(it) } + capturedVars.forEach { nonConstructorUsageTracker!!.used(it) } val closureArgs = capturedVars.map { - val name = nonConstructorUsageTracker.getNameForCapturedDescriptor(it)!! + val name = nonConstructorUsageTracker!!.getNameForCapturedDescriptor(it)!! JsAstUtils.pureFqn(name, closureQualifier) } callSite.invocationArgs.addAll(0, closureArgs) @@ -332,12 +344,12 @@ class ClassTranslator private constructor( private fun addClosureParameters(constructor: ConstructorInfo, nonConstructorContext: TranslationContext) { val usageTracker = constructor.context.usageTracker()!! val capturedVars = context().getClassOrConstructorClosure(constructor.descriptor) ?: return - val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!! + val nonConstructorUsageTracker = nonConstructorContext.usageTracker() val function = constructor.function val additionalStatements = mutableListOf() for ((i, capturedVar) in capturedVars.withIndex()) { - val fieldName = nonConstructorUsageTracker.capturedDescriptorToJsName[capturedVar] + val fieldName = nonConstructorUsageTracker?.capturedDescriptorToJsName?.get(capturedVar) val name = usageTracker.capturedDescriptorToJsName[capturedVar] ?: fieldName!! function.parameters.add(i, JsParameter(name)) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java index 27a855949d5..c0a501b69de 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/initializer/ClassInitializerTranslator.java @@ -192,11 +192,11 @@ public final class ClassInitializerTranslator extends AbstractTranslator { List superclassClosure = context.getClassOrConstructorClosure(superDescriptor); if (superclassClosure != null) { UsageTracker tracker = context.usageTracker(); - assert tracker != null : "Closure exists, therefore UsageTracker must exist too. Translating constructor of " + - classDescriptor; - for (DeclarationDescriptor capturedValue : superclassClosure) { - tracker.used(capturedValue); - arguments.add(tracker.getCapturedDescriptorToJsName().get(capturedValue).makeRef()); + if (tracker != null) { + for (DeclarationDescriptor capturedValue : superclassClosure) { + tracker.used(capturedValue); + arguments.add(tracker.getCapturedDescriptorToJsName().get(capturedValue).makeRef()); + } } }