JS: when translating nested classes of local classes, capture closure variables via outermost local class. See KT-12566
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// Enable when KT-12566 gets fixed
|
||||
|
||||
fun box(): String {
|
||||
var log = ""
|
||||
|
||||
|
||||
+1
-7
@@ -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")
|
||||
|
||||
+36
-4
@@ -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;
|
||||
|
||||
|
||||
+20
-8
@@ -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<JsStatement>()
|
||||
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))
|
||||
|
||||
+5
-5
@@ -192,11 +192,11 @@ public final class ClassInitializerTranslator extends AbstractTranslator {
|
||||
List<DeclarationDescriptor> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user