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>();
|
Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>();
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
boolean capturesAnonymousObjectThatMustBeRegenerated = false;
|
||||||
for (int i = 0; i < paramCount; i++) {
|
for (int i = 0; i < paramCount; i++) {
|
||||||
SourceValue sourceValue = frame.getStack(firstParameterIndex + i);
|
SourceValue sourceValue = frame.getStack(firstParameterIndex + i);
|
||||||
LambdaInfo lambdaInfo = MethodInlinerUtilKt.getLambdaIfExistsAndMarkInstructions(
|
LambdaInfo lambdaInfo = MethodInlinerUtilKt.getLambdaIfExistsAndMarkInstructions(
|
||||||
@@ -471,12 +472,17 @@ public class MethodInliner {
|
|||||||
if (lambdaInfo != null) {
|
if (lambdaInfo != null) {
|
||||||
lambdaMapping.put(offset, lambdaInfo);
|
lambdaMapping.put(offset, lambdaInfo);
|
||||||
}
|
}
|
||||||
|
else if (i < argTypes.length && isAnonymousClassThatMustBeRegenerated(argTypes[i])) {
|
||||||
|
capturesAnonymousObjectThatMustBeRegenerated = true;
|
||||||
|
}
|
||||||
|
|
||||||
offset += i == 0 ? 1 : argTypes[i - 1].getSize();
|
offset += i == 0 ? 1 : argTypes[i - 1].getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
transformations.add(
|
transformations.add(
|
||||||
buildConstructorInvocation(owner, desc, lambdaMapping, awaitClassReification)
|
buildConstructorInvocation(
|
||||||
|
owner, desc, lambdaMapping, awaitClassReification, capturesAnonymousObjectThatMustBeRegenerated
|
||||||
|
)
|
||||||
);
|
);
|
||||||
awaitClassReification = false;
|
awaitClassReification = false;
|
||||||
}
|
}
|
||||||
@@ -538,6 +544,12 @@ public class MethodInliner {
|
|||||||
return node;
|
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
|
@NotNull
|
||||||
private Frame<SourceValue>[] analyzeMethodNodeBeforeInline(@NotNull MethodNode node) {
|
private Frame<SourceValue>[] analyzeMethodNodeBeforeInline(@NotNull MethodNode node) {
|
||||||
try {
|
try {
|
||||||
@@ -584,7 +596,8 @@ public class MethodInliner {
|
|||||||
@NotNull String anonymousType,
|
@NotNull String anonymousType,
|
||||||
@NotNull String desc,
|
@NotNull String desc,
|
||||||
@NotNull Map<Integer, LambdaInfo> lambdaMapping,
|
@NotNull Map<Integer, LambdaInfo> lambdaMapping,
|
||||||
boolean needReification
|
boolean needReification,
|
||||||
|
boolean capturesAnonymousObjectThatMustBeRegenerated
|
||||||
) {
|
) {
|
||||||
boolean memoizeAnonymousObject = inliningContext.findAnonymousObjectTransformationInfo(anonymousType) == null;
|
boolean memoizeAnonymousObject = inliningContext.findAnonymousObjectTransformationInfo(anonymousType) == null;
|
||||||
|
|
||||||
@@ -594,7 +607,8 @@ public class MethodInliner {
|
|||||||
isAlreadyRegenerated(anonymousType),
|
isAlreadyRegenerated(anonymousType),
|
||||||
desc,
|
desc,
|
||||||
false,
|
false,
|
||||||
inliningContext.nameGenerator
|
inliningContext.nameGenerator,
|
||||||
|
capturesAnonymousObjectThatMustBeRegenerated
|
||||||
);
|
);
|
||||||
|
|
||||||
if (memoizeAnonymousObject) {
|
if (memoizeAnonymousObject) {
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ class AnonymousObjectTransformationInfo internal constructor(
|
|||||||
private val alreadyRegenerated: Boolean,
|
private val alreadyRegenerated: Boolean,
|
||||||
val constructorDesc: String?,
|
val constructorDesc: String?,
|
||||||
private val isStaticOrigin: Boolean,
|
private val isStaticOrigin: Boolean,
|
||||||
parentNameGenerator: NameGenerator
|
parentNameGenerator: NameGenerator,
|
||||||
|
private val capturesAnonymousObjectThatMustBeRegenerated: Boolean = false
|
||||||
) : TransformationInfo {
|
) : TransformationInfo {
|
||||||
|
|
||||||
override val nameGenerator by lazy {
|
override val nameGenerator by lazy {
|
||||||
@@ -93,7 +94,8 @@ class AnonymousObjectTransformationInfo internal constructor(
|
|||||||
) : this(ownerInternalName, needReification, hashMapOf(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator)
|
) : this(ownerInternalName, needReification, hashMapOf(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator)
|
||||||
|
|
||||||
override fun shouldRegenerate(sameModule: Boolean): Boolean =
|
override fun shouldRegenerate(sameModule: Boolean): Boolean =
|
||||||
!alreadyRegenerated && (!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification)
|
!alreadyRegenerated &&
|
||||||
|
(!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification || capturesAnonymousObjectThatMustBeRegenerated)
|
||||||
|
|
||||||
override fun canRemoveAfterTransformation(): Boolean {
|
override fun canRemoveAfterTransformation(): Boolean {
|
||||||
// Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC within lambda
|
// 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);
|
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")
|
@TestMetadata("safeCall.kt")
|
||||||
public void testSafeCall() throws Exception {
|
public void testSafeCall() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/safeCall.kt");
|
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);
|
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")
|
@TestMetadata("localFunInLambda.kt")
|
||||||
public void testLocalFunInLambda() throws Exception {
|
public void testLocalFunInLambda() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/localFunInLambda/localFunInLambda.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/localFunInLambda/localFunInLambda.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("compiler/testData/codegen/boxInline/multifileClasses")
|
||||||
|
|||||||
+18
@@ -200,6 +200,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("safeCall.kt")
|
||||||
public void testSafeCall() throws Exception {
|
public void testSafeCall() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/safeCall.kt");
|
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);
|
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")
|
@TestMetadata("localFunInLambda.kt")
|
||||||
public void testLocalFunInLambda() throws Exception {
|
public void testLocalFunInLambda() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/localFunInLambda/localFunInLambda.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/localFunInLambda/localFunInLambda.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("compiler/testData/codegen/boxInline/multifileClasses")
|
||||||
|
|||||||
Reference in New Issue
Block a user