Fix inline codegen on local functions inside inlined lambda
The problem was that anonymous classes wasn't regenerated although they capture another anonymous class that is a subject for regeneration #KT-8689 Fixed
This commit is contained in:
@@ -463,6 +463,7 @@ public class MethodInliner {
|
||||
Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>();
|
||||
|
||||
int offset = 0;
|
||||
boolean capturesAnonymousObjectThatMustBeRegenerated = false;
|
||||
for (int i = 0; i < paramCount; i++) {
|
||||
SourceValue sourceValue = frame.getStack(firstParameterIndex + i);
|
||||
LambdaInfo lambdaInfo = MethodInlinerUtilKt.getLambdaIfExistsAndMarkInstructions(
|
||||
@@ -471,12 +472,17 @@ public class MethodInliner {
|
||||
if (lambdaInfo != null) {
|
||||
lambdaMapping.put(offset, lambdaInfo);
|
||||
}
|
||||
else if (i < argTypes.length && isAnonymousClassThatMustBeRegenerated(argTypes[i])) {
|
||||
capturesAnonymousObjectThatMustBeRegenerated = true;
|
||||
}
|
||||
|
||||
offset += i == 0 ? 1 : argTypes[i - 1].getSize();
|
||||
}
|
||||
|
||||
transformations.add(
|
||||
buildConstructorInvocation(owner, desc, lambdaMapping, awaitClassReification)
|
||||
buildConstructorInvocation(
|
||||
owner, desc, lambdaMapping, awaitClassReification, capturesAnonymousObjectThatMustBeRegenerated
|
||||
)
|
||||
);
|
||||
awaitClassReification = false;
|
||||
}
|
||||
@@ -538,6 +544,12 @@ public class MethodInliner {
|
||||
return node;
|
||||
}
|
||||
|
||||
private boolean isAnonymousClassThatMustBeRegenerated(@Nullable Type type) {
|
||||
if (type == null || type.getSort() != Type.OBJECT) return false;
|
||||
AnonymousObjectTransformationInfo info = inliningContext.findAnonymousObjectTransformationInfo(type.getInternalName());
|
||||
return info != null && info.shouldRegenerate(true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Frame<SourceValue>[] analyzeMethodNodeBeforeInline(@NotNull MethodNode node) {
|
||||
try {
|
||||
@@ -584,7 +596,8 @@ public class MethodInliner {
|
||||
@NotNull String anonymousType,
|
||||
@NotNull String desc,
|
||||
@NotNull Map<Integer, LambdaInfo> lambdaMapping,
|
||||
boolean needReification
|
||||
boolean needReification,
|
||||
boolean capturesAnonymousObjectThatMustBeRegenerated
|
||||
) {
|
||||
boolean memoizeAnonymousObject = inliningContext.findAnonymousObjectTransformationInfo(anonymousType) == null;
|
||||
|
||||
@@ -594,7 +607,8 @@ public class MethodInliner {
|
||||
isAlreadyRegenerated(anonymousType),
|
||||
desc,
|
||||
false,
|
||||
inliningContext.nameGenerator
|
||||
inliningContext.nameGenerator,
|
||||
capturesAnonymousObjectThatMustBeRegenerated
|
||||
);
|
||||
|
||||
if (memoizeAnonymousObject) {
|
||||
|
||||
@@ -68,7 +68,8 @@ class AnonymousObjectTransformationInfo internal constructor(
|
||||
private val alreadyRegenerated: Boolean,
|
||||
val constructorDesc: String?,
|
||||
private val isStaticOrigin: Boolean,
|
||||
parentNameGenerator: NameGenerator
|
||||
parentNameGenerator: NameGenerator,
|
||||
private val capturesAnonymousObjectThatMustBeRegenerated: Boolean = false
|
||||
) : TransformationInfo {
|
||||
|
||||
override val nameGenerator by lazy {
|
||||
@@ -93,7 +94,8 @@ class AnonymousObjectTransformationInfo internal constructor(
|
||||
) : this(ownerInternalName, needReification, hashMapOf(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator)
|
||||
|
||||
override fun shouldRegenerate(sameModule: Boolean): Boolean =
|
||||
!alreadyRegenerated && (!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification)
|
||||
!alreadyRegenerated &&
|
||||
(!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification || capturesAnonymousObjectThatMustBeRegenerated)
|
||||
|
||||
override fun canRemoveAfterTransformation(): Boolean {
|
||||
// Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC within lambda
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// FILE: 1.kt
|
||||
package test
|
||||
|
||||
public inline fun myRun(block: () -> Unit) {
|
||||
return block()
|
||||
}
|
||||
|
||||
// FILE: 2.kt
|
||||
|
||||
//NO_CHECK_LAMBDA_INLINING
|
||||
import test.*
|
||||
|
||||
fun box(): String {
|
||||
var res = ""
|
||||
myRun {
|
||||
val x = object {
|
||||
fun foo() {
|
||||
res = "OK"
|
||||
}
|
||||
}
|
||||
object {
|
||||
fun bar() = x.foo()
|
||||
}.bar()
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// FILE: 1.kt
|
||||
package test
|
||||
|
||||
public inline fun myRun(block: () -> Unit) {
|
||||
return block()
|
||||
}
|
||||
|
||||
// FILE: 2.kt
|
||||
|
||||
//NO_CHECK_LAMBDA_INLINING
|
||||
import test.*
|
||||
|
||||
fun box(): String {
|
||||
var res = ""
|
||||
myRun {
|
||||
fun f1() {
|
||||
res = "OK"
|
||||
}
|
||||
val x: () -> Unit = {
|
||||
f1()
|
||||
}
|
||||
|
||||
x()
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// FILE: 1.kt
|
||||
package test
|
||||
|
||||
public inline fun myRun(block: () -> Unit) {
|
||||
return block()
|
||||
}
|
||||
|
||||
// FILE: 2.kt
|
||||
|
||||
//NO_CHECK_LAMBDA_INLINING
|
||||
import test.*
|
||||
|
||||
fun box(): String {
|
||||
var res = ""
|
||||
myRun {
|
||||
fun f1() {
|
||||
res = "OK"
|
||||
}
|
||||
fun f2() {
|
||||
f1()
|
||||
}
|
||||
f2()
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
@@ -200,6 +200,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("objectInLambdaCapturesAnotherObject.kt")
|
||||
public void testObjectInLambdaCapturesAnotherObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/objectInLambdaCapturesAnotherObject.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("safeCall.kt")
|
||||
public void testSafeCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/safeCall.kt");
|
||||
@@ -1151,11 +1157,23 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/localFunInLambda"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaInLambdaCapturesAnotherFun.kt")
|
||||
public void testLambdaInLambdaCapturesAnotherFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/localFunInLambda/lambdaInLambdaCapturesAnotherFun.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localFunInLambda.kt")
|
||||
public void testLocalFunInLambda() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/localFunInLambda/localFunInLambda.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localFunInLambdaCapturesAnotherFun.kt")
|
||||
public void testLocalFunInLambdaCapturesAnotherFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/localFunInLambda/localFunInLambdaCapturesAnotherFun.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/multifileClasses")
|
||||
|
||||
+18
@@ -200,6 +200,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("objectInLambdaCapturesAnotherObject.kt")
|
||||
public void testObjectInLambdaCapturesAnotherObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/objectInLambdaCapturesAnotherObject.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("safeCall.kt")
|
||||
public void testSafeCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/safeCall.kt");
|
||||
@@ -1151,11 +1157,23 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/localFunInLambda"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaInLambdaCapturesAnotherFun.kt")
|
||||
public void testLambdaInLambdaCapturesAnotherFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/localFunInLambda/lambdaInLambdaCapturesAnotherFun.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localFunInLambda.kt")
|
||||
public void testLocalFunInLambda() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/localFunInLambda/localFunInLambda.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("localFunInLambdaCapturesAnotherFun.kt")
|
||||
public void testLocalFunInLambdaCapturesAnotherFun() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/localFunInLambda/localFunInLambdaCapturesAnotherFun.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/multifileClasses")
|
||||
|
||||
Reference in New Issue
Block a user