KT-11030 Fix referencing wrong field generated for captured variable when this captured variable referenced both from constructor and non-constructor method. Avoid duplicate generation of call to delegated constructor.
This commit is contained in:
+9
@@ -0,0 +1,9 @@
|
|||||||
|
class A(val f: () -> Int) {
|
||||||
|
constructor() : this({ 23 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val result = A().f()
|
||||||
|
if (result != 23) return "fail: $result"
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -12868,6 +12868,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("delegatedThisWithLambda.kt")
|
||||||
|
public void testDelegatedThisWithLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/delegatedThisWithLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("delegationWithPrimary.kt")
|
@TestMetadata("delegationWithPrimary.kt")
|
||||||
public void testDelegationWithPrimary() throws Exception {
|
public void testDelegationWithPrimary() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/delegationWithPrimary.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/delegationWithPrimary.kt");
|
||||||
|
|||||||
@@ -167,4 +167,8 @@ public final class ClosureTest extends SingleFileTranslationTest {
|
|||||||
public void testClosureThisAndClassObject() throws Exception {
|
public void testClosureThisAndClassObject() throws Exception {
|
||||||
checkFooBoxIsOk();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testLocalConstructorAndMethod() throws Exception {
|
||||||
|
checkFooBoxIsOk();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -119,6 +119,12 @@ public class SecondaryConstructorTestGenerated extends AbstractSecondaryConstruc
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("delegatedThisWithLambda.kt")
|
||||||
|
public void testDelegatedThisWithLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/delegatedThisWithLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("delegationWithPrimary.kt")
|
@TestMetadata("delegationWithPrimary.kt")
|
||||||
public void testDelegationWithPrimary() throws Exception {
|
public void testDelegationWithPrimary() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/delegationWithPrimary.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/secondaryConstructors/delegationWithPrimary.kt");
|
||||||
|
|||||||
+5
-1
@@ -453,7 +453,11 @@ public class TranslationContext {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public List<DeclarationDescriptor> getLocalClassClosure(@NotNull MemberDescriptor localClass) {
|
public List<DeclarationDescriptor> getLocalClassClosure(@NotNull MemberDescriptor localClass) {
|
||||||
return staticContext.getLocalClassClosure(localClass);
|
List<DeclarationDescriptor> result = staticContext.getLocalClassClosure(localClass);
|
||||||
|
if (result == null && localClass instanceof ConstructorDescriptor && ((ConstructorDescriptor) localClass).isPrimary()) {
|
||||||
|
result = staticContext.getLocalClassClosure((ClassDescriptor) localClass.getContainingDeclaration());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
+35
-39
@@ -109,15 +109,11 @@ class ClassTranslator private constructor(
|
|||||||
|
|
||||||
val dataClassGenerator = JsDataClassGenerator(classDeclaration, context, properties)
|
val dataClassGenerator = JsDataClassGenerator(classDeclaration, context, properties)
|
||||||
|
|
||||||
inflateClosure()
|
emitConstructors(nonConstructorContext)
|
||||||
for (constructor in allConstructors) {
|
for (constructor in allConstructors) {
|
||||||
addClosureParameters(constructor, nonConstructorContext, dataClassGenerator)
|
addClosureParameters(constructor, nonConstructorContext, dataClassGenerator)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (constructor in allConstructors) {
|
|
||||||
constructor.superCallGenerator()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.isData) {
|
if (descriptor.isData) {
|
||||||
dataClassGenerator.generate()
|
dataClassGenerator.generate()
|
||||||
}
|
}
|
||||||
@@ -147,7 +143,8 @@ class ClassTranslator private constructor(
|
|||||||
private fun translatePrimaryConstructor(classContext: TranslationContext, delegationTranslator: DelegationTranslator) {
|
private fun translatePrimaryConstructor(classContext: TranslationContext, delegationTranslator: DelegationTranslator) {
|
||||||
if (isTrait()) return
|
if (isTrait()) return
|
||||||
|
|
||||||
var constructorContext = classContext.innerWithUsageTracker(classContext.scope(), descriptor)
|
val scope = JsFunctionScope(classContext.scope(), "$descriptor: primary constructor")
|
||||||
|
var constructorContext = classContext.innerWithUsageTracker(scope, descriptor)
|
||||||
val initializer = ClassInitializerTranslator(classDeclaration, constructorContext).generateInitializeMethod(delegationTranslator)
|
val initializer = ClassInitializerTranslator(classDeclaration, constructorContext).generateInitializeMethod(delegationTranslator)
|
||||||
invocationArguments += initializer
|
invocationArguments += initializer
|
||||||
|
|
||||||
@@ -159,9 +156,9 @@ class ClassTranslator private constructor(
|
|||||||
val constructorDescriptor = BindingUtils.getDescriptorForElement(classContext.bindingContext(), constructor) as ConstructorDescriptor
|
val constructorDescriptor = BindingUtils.getDescriptorForElement(classContext.bindingContext(), constructor) as ConstructorDescriptor
|
||||||
val classDescriptor = constructorDescriptor.containingDeclaration
|
val classDescriptor = constructorDescriptor.containingDeclaration
|
||||||
|
|
||||||
var context = classContext.innerWithUsageTracker(classContext.scope(), constructorDescriptor)
|
val constructorScope = classContext.getScopeForDescriptor(constructorDescriptor)
|
||||||
|
var context = classContext.innerWithUsageTracker(constructorScope, constructorDescriptor)
|
||||||
|
|
||||||
val constructorScope = context.getScopeForDescriptor(constructorDescriptor)
|
|
||||||
val thisName = constructorScope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME)
|
val thisName = constructorScope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME)
|
||||||
val thisNameRef = thisName.makeRef()
|
val thisNameRef = thisName.makeRef()
|
||||||
val receiverDescriptor = classDescriptor.thisAsReceiverParameter
|
val receiverDescriptor = classDescriptor.thisAsReceiverParameter
|
||||||
@@ -204,16 +201,10 @@ class ClassTranslator private constructor(
|
|||||||
// Generate super/this call to insert to beginning of the function
|
// Generate super/this call to insert to beginning of the function
|
||||||
val resolvedCall = BindingContextUtils.getDelegationConstructorCall(context.bindingContext(), constructorDescriptor)
|
val resolvedCall = BindingContextUtils.getDelegationConstructorCall(context.bindingContext(), constructorDescriptor)
|
||||||
val delegationClassDescriptor = resolvedCall?.resultingDescriptor?.containingDeclaration
|
val delegationClassDescriptor = resolvedCall?.resultingDescriptor?.containingDeclaration
|
||||||
val superCall = if (resolvedCall != null && !KotlinBuiltIns.isAny(delegationClassDescriptor!!)) {
|
|
||||||
CallTranslator.translate(context, resolvedCall)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (superCall != null) {
|
if (resolvedCall != null && !KotlinBuiltIns.isAny(delegationClassDescriptor!!)) {
|
||||||
superCallGenerators += {
|
superCallGenerators += {
|
||||||
it += CallTranslator.translate(context, resolvedCall!!).toInvocationWith(leadingArgs, thisNameRef).makeStmt()
|
it += CallTranslator.translate(context, resolvedCall).toInvocationWith(leadingArgs, thisNameRef).makeStmt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,9 +239,9 @@ class ClassTranslator private constructor(
|
|||||||
return if (primary != null) sequenceOf(primary) + secondaryConstructors else secondaryConstructors.asSequence()
|
return if (primary != null) sequenceOf(primary) + secondaryConstructors else secondaryConstructors.asSequence()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inflateClosure() {
|
private fun emitConstructors(nonConstructorContext: TranslationContext) {
|
||||||
// Build map that maps constructor to all constructors called via this()
|
// Build map that maps constructor to all constructors called via this()
|
||||||
val constructorMap = allConstructors.map { Pair(it.descriptor, it) }.toMap()
|
val constructorMap = allConstructors.map { it.descriptor to it }.toMap()
|
||||||
val primaryConstructor = this.primaryConstructor
|
val primaryConstructor = this.primaryConstructor
|
||||||
|
|
||||||
val thisCalls = allConstructors.map {
|
val thisCalls = allConstructors.map {
|
||||||
@@ -286,9 +277,27 @@ class ClassTranslator private constructor(
|
|||||||
|
|
||||||
allConstructors.forEach(::sort)
|
allConstructors.forEach(::sort)
|
||||||
|
|
||||||
// Inflate closure
|
// Emit constructors
|
||||||
for (constructor in sortedConstructors) {
|
for (constructor in sortedConstructors) {
|
||||||
|
constructor.superCallGenerator()
|
||||||
|
|
||||||
|
val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!!
|
||||||
val usageTracker = constructor.context.usageTracker() ?: continue
|
val usageTracker = constructor.context.usageTracker() ?: continue
|
||||||
|
|
||||||
|
val nonConstructorCapturedVars = nonConstructorUsageTracker.capturedDescriptors
|
||||||
|
val constructorCapturedVars = usageTracker.capturedDescriptors
|
||||||
|
|
||||||
|
val capturedVars = (nonConstructorCapturedVars.asSequence() +
|
||||||
|
constructorCapturedVars.asSequence().filter { it !in nonConstructorCapturedVars }).toList()
|
||||||
|
|
||||||
|
val descriptor = constructor.descriptor
|
||||||
|
nonConstructorContext.putLocalClassClosure(descriptor, capturedVars)
|
||||||
|
if (descriptor is ClassDescriptor) {
|
||||||
|
if (primaryConstructor != null) {
|
||||||
|
nonConstructorContext.putLocalClassClosure(primaryConstructor.descriptor, capturedVars)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (thisCall in thisCalls[constructor].orEmpty()) {
|
for (thisCall in thisCalls[constructor].orEmpty()) {
|
||||||
val callUsageTracker = thisCall.context.usageTracker() ?: continue
|
val callUsageTracker = thisCall.context.usageTracker() ?: continue
|
||||||
callUsageTracker.capturedDescriptors.forEach { usageTracker.used(it) }
|
callUsageTracker.capturedDescriptors.forEach { usageTracker.used(it) }
|
||||||
@@ -299,31 +308,18 @@ class ClassTranslator private constructor(
|
|||||||
private fun addClosureParameters(constructor: ConstructorInfo, nonConstructorContext: TranslationContext,
|
private fun addClosureParameters(constructor: ConstructorInfo, nonConstructorContext: TranslationContext,
|
||||||
dataClassGenerator: JsDataClassGenerator) {
|
dataClassGenerator: JsDataClassGenerator) {
|
||||||
val usageTracker = constructor.context.usageTracker()!!
|
val usageTracker = constructor.context.usageTracker()!!
|
||||||
|
val capturedVars = context().getLocalClassClosure(constructor.descriptor) ?: return
|
||||||
val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!!
|
val nonConstructorUsageTracker = nonConstructorContext.usageTracker()!!
|
||||||
|
|
||||||
val nonConstructorCapturedVars = nonConstructorUsageTracker.capturedDescriptors
|
|
||||||
val constructorCapturedVars = usageTracker.capturedDescriptors
|
|
||||||
|
|
||||||
val capturedVars = (nonConstructorCapturedVars.asSequence() +
|
|
||||||
constructorCapturedVars.asSequence().filter { it !in nonConstructorCapturedVars }).toList()
|
|
||||||
|
|
||||||
val descriptor = constructor.descriptor
|
|
||||||
nonConstructorContext.putLocalClassClosure(descriptor, capturedVars)
|
|
||||||
if (descriptor is ClassDescriptor) {
|
|
||||||
val primaryConstructor = descriptor.constructors.find { it.isPrimary }
|
|
||||||
if (primaryConstructor != null) {
|
|
||||||
nonConstructorContext.putLocalClassClosure(primaryConstructor, capturedVars)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val function = constructor.function
|
val function = constructor.function
|
||||||
for ((i, capturedVar) in capturedVars.withIndex()) {
|
for ((i, capturedVar) in capturedVars.withIndex()) {
|
||||||
val name = usageTracker.capturedDescriptorToJsName[capturedVar] ?:
|
val fieldName = nonConstructorUsageTracker.capturedDescriptorToJsName[capturedVar]
|
||||||
nonConstructorUsageTracker.capturedDescriptorToJsName[capturedVar]!!
|
val name = usageTracker.capturedDescriptorToJsName[capturedVar] ?: fieldName!!
|
||||||
|
|
||||||
function.parameters.add(i, JsParameter(name))
|
function.parameters.add(i, JsParameter(name))
|
||||||
if (capturedVar in nonConstructorUsageTracker.capturedDescriptors && constructor == primaryConstructor) {
|
if (fieldName != null && constructor == primaryConstructor) {
|
||||||
function.body.statements.add(i, JsAstUtils.defineSimpleProperty(name.ident, name.makeRef()))
|
function.body.statements.add(i, JsAstUtils.defineSimpleProperty(fieldName.ident, name.makeRef()))
|
||||||
dataClassGenerator.addClosureVariable(name)
|
dataClassGenerator.addClosureVariable(fieldName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
interface B {
|
||||||
|
fun result(): Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class A(private val x: Int) {
|
||||||
|
fun test() = object : B {
|
||||||
|
val y = x + 1
|
||||||
|
|
||||||
|
override fun result() = x * 10 + y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals(23, A(2).test().result())
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user