JVM: rename this$0 when regenerating nested objects too
In the old backend, this was unnecessary because nested objects would reference their lambdas' captures through the original this$0. On JVM_IR, using loose capture fields means a name/descriptor clash can occur on any level of nesting, not just the top.
This commit is contained in:
+38
-56
@@ -462,6 +462,29 @@ class AnonymousObjectTransformer(
|
||||
val indexToFunctionalArgument = transformationInfo.functionalArguments
|
||||
val capturedParams = HashSet<Int>()
|
||||
|
||||
// Possible cases where we need to add each lambda's captures separately:
|
||||
//
|
||||
// 1. Top-level object in an inline lambda that is *not* being inlined into another object. In this case, we
|
||||
// have no choice but to add a separate field for each captured variable. `capturedLambdas` is either empty
|
||||
// (already have the fields) or contains the parent lambda object (captures used to be read from it, but
|
||||
// the object will be removed and its contents inlined).
|
||||
//
|
||||
// 2. Top-level object in a named inline function. Again, there's no option but to add separate fields.
|
||||
// `capturedLambdas` contains all lambdas used by this object and nested objects.
|
||||
//
|
||||
// 3. Nested object, either in an inline lambda or an inline function. This case has two subcases:
|
||||
// * The object's captures are passed as separate arguments (e.g. KT-28064 style object that used to be in a lambda);
|
||||
// we *could* group them into `this$0` now, but choose not to. Lambdas are replaced by their captures to match.
|
||||
// * The object's captures are already grouped into `this$0`; this includes captured lambda parameters (for objects in
|
||||
// inline functions) and a reference to the outer object or lambda (for objects in lambdas), so `capturedLambdas` is
|
||||
// empty anyway.
|
||||
//
|
||||
// The only remaining case is a top-level object inside a (crossinline) lambda that is inlined into another object.
|
||||
// Then, the reference to the soon-to-be-removed lambda class containing the captures (and it exists, or else the object
|
||||
// would not have needed regeneration in the first place) is simply replaced with a reference to the outer object, and
|
||||
// that object will contain loose fields for everything we need to capture.
|
||||
val topLevelInCrossinlineLambda = parentFieldRemapper is InlinedLambdaRemapper && !parentFieldRemapper.parent!!.isRoot
|
||||
|
||||
//load captured parameters and patch instruction list
|
||||
// NB: there is also could be object fields
|
||||
val toDelete = arrayListOf<AbstractInsnNode>()
|
||||
@@ -470,10 +493,12 @@ class AnonymousObjectTransformer(
|
||||
val parameterAload = fieldNode.previous as VarInsnNode
|
||||
val varIndex = parameterAload.`var`
|
||||
val functionalArgument = indexToFunctionalArgument[varIndex]
|
||||
val newFieldName = if (isThis0(fieldName) && shouldRenameThis0(parentFieldRemapper, indexToFunctionalArgument.values))
|
||||
getNewFieldName(fieldName, true)
|
||||
else
|
||||
fieldName
|
||||
// If an outer `this` is already captured by this object, rename it if any inline lambda will capture
|
||||
// one of the same type, causing the code below to create a clash. Note that the values can be different.
|
||||
// TODO: this is only really necessary if there will be a name *and* type clash.
|
||||
val shouldRename = !topLevelInCrossinlineLambda && isThis0(fieldName) &&
|
||||
indexToFunctionalArgument.values.any { it is LambdaInfo && it.capturedVars.any { it.fieldName == fieldName } }
|
||||
val newFieldName = if (shouldRename) addUniqueField(fieldName + INLINE_FUN_THIS_0_SUFFIX) else fieldName
|
||||
val info = capturedParamBuilder.addCapturedParam(
|
||||
Type.getObjectType(transformationInfo.oldClassName), fieldName, newFieldName,
|
||||
Type.getType(fieldNode.desc), functionalArgument is LambdaInfo, null
|
||||
@@ -508,35 +533,17 @@ class AnonymousObjectTransformer(
|
||||
//For all inlined lambdas add their captured parameters
|
||||
//TODO: some of such parameters could be skipped - we should perform additional analysis
|
||||
val allRecapturedParameters = ArrayList<CapturedParamDesc>()
|
||||
if (parentFieldRemapper !is InlinedLambdaRemapper || parentFieldRemapper.parent!!.isRoot) {
|
||||
// Possible cases:
|
||||
//
|
||||
// 1. Top-level object in an inline lambda that is *not* being inlined into another object. In this case, we
|
||||
// have no choice but to add a separate field for each captured variable. `capturedLambdas` is either empty
|
||||
// (already have the fields) or contains the parent lambda object (captures used to be read from it, but
|
||||
// the object will be removed and its contents inlined).
|
||||
//
|
||||
// 2. Top-level object in a named inline function. Again, there's no option but to add separate fields.
|
||||
// `capturedLambdas` contains all lambdas used by this object and nested objects.
|
||||
//
|
||||
// 3. Nested object, either in an inline lambda or an inline function. This case has two subcases:
|
||||
// * The object's captures are passed as separate arguments (e.g. KT-28064 style object that used to be in a lambda);
|
||||
// we could group them into `this$0` now, but choose not to. Lambdas are replaced by their captures.
|
||||
// * The object's captures are already grouped into `this$0`; this includes captured lambda parameters (for objects in
|
||||
// inline functions) and a reference to the outer object or lambda (for objects in lambdas), so `capturedLambdas` is
|
||||
// empty and the choice doesn't matter.
|
||||
//
|
||||
val alreadyAdded = HashMap<String, CapturedParamInfo>()
|
||||
if (!topLevelInCrossinlineLambda) {
|
||||
val capturedOuterThisTypes = mutableSetOf<String>()
|
||||
for (info in capturedLambdas) {
|
||||
for (desc in info.capturedVars) {
|
||||
val key = desc.fieldName + "$$$" + desc.type.className
|
||||
val alreadyAddedParam = alreadyAdded[key]
|
||||
|
||||
val recapturedParamInfo = capturedParamBuilder.addCapturedParam(
|
||||
desc,
|
||||
alreadyAddedParam?.newFieldName ?: getNewFieldName(desc.fieldName, false),
|
||||
alreadyAddedParam != null
|
||||
)
|
||||
// Merge all outer `this` of the same type captured by inlined lambdas, since they have to be the same
|
||||
// object. Outer `this` captured by the original object itself should have been renamed above,
|
||||
// and can have a different value even if the same type is captured by a lambda.
|
||||
val recapturedParamInfo = if (isThis0(desc.fieldName))
|
||||
capturedParamBuilder.addCapturedParam(desc, desc.fieldName, !capturedOuterThisTypes.add(desc.type.className))
|
||||
else
|
||||
capturedParamBuilder.addCapturedParam(desc, addUniqueField(desc.fieldName + INLINE_TRANSFORMATION_SUFFIX), false)
|
||||
if (info is ExpressionLambda && info.isCapturedSuspend(desc)) {
|
||||
recapturedParamInfo.functionalArgument = NonInlineableArgumentForInlineableParameterCalledInSuspend
|
||||
}
|
||||
@@ -551,10 +558,6 @@ class AnonymousObjectTransformer(
|
||||
allRecapturedParameters.add(desc)
|
||||
|
||||
constructorParamBuilder.addCapturedParam(recapturedParamInfo, recapturedParamInfo.newFieldName).remapValue = composed
|
||||
|
||||
if (isThis0(desc.fieldName)) {
|
||||
alreadyAdded.put(key, recapturedParamInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (capturedLambdas.isNotEmpty()) {
|
||||
@@ -579,24 +582,6 @@ class AnonymousObjectTransformer(
|
||||
return constructorAdditionalFakeParams
|
||||
}
|
||||
|
||||
private fun shouldRenameThis0(parentFieldRemapper: FieldRemapper, values: Collection<FunctionalArgument>): Boolean {
|
||||
return if (isFirstDeclSiteLambdaFieldRemapper(parentFieldRemapper)) {
|
||||
values.any { it is LambdaInfo && it.capturedVars.any { isThis0(it.fieldName) } }
|
||||
} else false
|
||||
}
|
||||
|
||||
private fun getNewFieldName(oldName: String, originalField: Boolean): String {
|
||||
if (AsmUtil.CAPTURED_THIS_FIELD == oldName) {
|
||||
return if (!originalField) {
|
||||
oldName
|
||||
} else {
|
||||
//rename original 'this$0' in declaration site lambda (inside inline function) to use this$0 only for outer lambda/object access on call site
|
||||
addUniqueField(oldName + INLINE_FUN_THIS_0_SUFFIX)
|
||||
}
|
||||
}
|
||||
return addUniqueField(oldName + INLINE_TRANSFORMATION_SUFFIX)
|
||||
}
|
||||
|
||||
private fun addUniqueField(name: String): String {
|
||||
val existNames = fieldNames.getOrPut(name) { LinkedList() }
|
||||
val suffix = if (existNames.isEmpty()) "" else "$" + existNames.size
|
||||
@@ -604,7 +589,4 @@ class AnonymousObjectTransformer(
|
||||
existNames.add(newName)
|
||||
return newName
|
||||
}
|
||||
|
||||
private fun isFirstDeclSiteLambdaFieldRemapper(parentRemapper: FieldRemapper): Boolean =
|
||||
parentRemapper !is RegeneratedLambdaFieldRemapper && parentRemapper !is InlinedLambdaRemapper
|
||||
}
|
||||
|
||||
+12
@@ -647,6 +647,18 @@ public class FirBlackBoxInlineCodegenTestGenerated extends AbstractFirBlackBoxIn
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested.kt")
|
||||
public void testKt8668_nested() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested_2.kt")
|
||||
public void testKt8668_nested_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested_2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("twoDifferentDispatchReceivers.kt")
|
||||
public void testTwoDifferentDispatchReceivers() throws Exception {
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// FILE: 1.kt
|
||||
package test
|
||||
|
||||
class C(val x: String) {
|
||||
fun f(y: String) = C(y).g { x }
|
||||
|
||||
inline fun g(crossinline h: () -> String) =
|
||||
{ { h() + x }() }()
|
||||
}
|
||||
|
||||
// FILE: 2.kt
|
||||
import test.*
|
||||
|
||||
fun box() = C("O").f("K")
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
// FILE: 1.kt
|
||||
package test
|
||||
|
||||
class C(val x: String) {
|
||||
inline fun f(crossinline h: () -> String) = C("").g { x + h() }
|
||||
|
||||
inline fun g(crossinline h: () -> String) =
|
||||
{ { h() + x }() }()
|
||||
}
|
||||
|
||||
// FILE: 2.kt
|
||||
import test.*
|
||||
|
||||
fun box() = C("O").f { "K" }
|
||||
+12
@@ -647,6 +647,18 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested.kt")
|
||||
public void testKt8668_nested() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested_2.kt")
|
||||
public void testKt8668_nested_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested_2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("twoDifferentDispatchReceivers.kt")
|
||||
public void testTwoDifferentDispatchReceivers() throws Exception {
|
||||
|
||||
+12
@@ -647,6 +647,18 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested.kt")
|
||||
public void testKt8668_nested() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested_2.kt")
|
||||
public void testKt8668_nested_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested_2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("twoDifferentDispatchReceivers.kt")
|
||||
public void testTwoDifferentDispatchReceivers() throws Exception {
|
||||
|
||||
+12
@@ -647,6 +647,18 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested.kt")
|
||||
public void testKt8668_nested() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested_2.kt")
|
||||
public void testKt8668_nested_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested_2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("twoDifferentDispatchReceivers.kt")
|
||||
public void testTwoDifferentDispatchReceivers() throws Exception {
|
||||
|
||||
+12
@@ -647,6 +647,18 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested.kt")
|
||||
public void testKt8668_nested() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested_2.kt")
|
||||
public void testKt8668_nested_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested_2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("twoDifferentDispatchReceivers.kt")
|
||||
public void testTwoDifferentDispatchReceivers() throws Exception {
|
||||
|
||||
+12
@@ -647,6 +647,18 @@ public class JvmIrAgainstOldBoxInlineTestGenerated extends AbstractJvmIrAgainstO
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested.kt")
|
||||
public void testKt8668_nested() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested_2.kt")
|
||||
public void testKt8668_nested_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested_2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("twoDifferentDispatchReceivers.kt")
|
||||
public void testTwoDifferentDispatchReceivers() throws Exception {
|
||||
|
||||
+12
@@ -647,6 +647,18 @@ public class JvmOldAgainstIrBoxInlineTestGenerated extends AbstractJvmOldAgainst
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_3.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested.kt")
|
||||
public void testKt8668_nested() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt8668_nested_2.kt")
|
||||
public void testKt8668_nested_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested_2.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("twoDifferentDispatchReceivers.kt")
|
||||
public void testTwoDifferentDispatchReceivers() throws Exception {
|
||||
|
||||
+10
@@ -506,6 +506,16 @@ public class IrJsCodegenInlineES6TestGenerated extends AbstractIrJsCodegenInline
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt8668_nested.kt")
|
||||
public void testKt8668_nested() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt8668_nested_2.kt")
|
||||
public void testKt8668_nested_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested_2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("twoDifferentDispatchReceivers.kt")
|
||||
public void testTwoDifferentDispatchReceivers() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/twoDifferentDispatchReceivers.kt");
|
||||
|
||||
Generated
+10
@@ -506,6 +506,16 @@ public class IrJsCodegenInlineTestGenerated extends AbstractIrJsCodegenInlineTes
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt8668_nested.kt")
|
||||
public void testKt8668_nested() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt8668_nested_2.kt")
|
||||
public void testKt8668_nested_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested_2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("twoDifferentDispatchReceivers.kt")
|
||||
public void testTwoDifferentDispatchReceivers() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/twoDifferentDispatchReceivers.kt");
|
||||
|
||||
Generated
+10
@@ -506,6 +506,16 @@ public class JsCodegenInlineTestGenerated extends AbstractJsCodegenInlineTest {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt8668_nested.kt")
|
||||
public void testKt8668_nested() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt8668_nested_2.kt")
|
||||
public void testKt8668_nested_2() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/kt8668_nested_2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("twoDifferentDispatchReceivers.kt")
|
||||
public void testTwoDifferentDispatchReceivers() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/twoCapturedReceivers/twoDifferentDispatchReceivers.kt");
|
||||
|
||||
Reference in New Issue
Block a user