JVM: regenerate objects if they have been regenerated in parent contexts
This is a hack to work around the fact that type mappings should not be inherited by inlining contexts for lambdas called from anonymous objects. As the lambda can call the inline function again, this could produce a reference to the original object, which is remapped to a new type in the parent context. Unfortunately, there are many redundant `MethodRemapper`s between the lambda and the class file, so simply editing `TypeRemapper` does not work. Hence, this hack. For now. (Issue found by compiling IntelliJ IDEA BTW.)
This commit is contained in:
@@ -157,13 +157,8 @@ class MethodInliner(
|
||||
// MethodRemapper doesn't extends LocalVariablesSorter, but RemappingMethodAdapter does.
|
||||
// So wrapping with LocalVariablesSorter to keep old behavior
|
||||
// TODO: investigate LocalVariablesSorter removing (see also same code in RemappingClassBuilder.java)
|
||||
val remappingMethodAdapter = MethodRemapper(
|
||||
LocalVariablesSorter(
|
||||
resultNode.access,
|
||||
resultNode.desc,
|
||||
wrapWithMaxLocalCalc(resultNode)
|
||||
), AsmTypeRemapper(remapper, result)
|
||||
)
|
||||
val localVariablesSorter = LocalVariablesSorter(resultNode.access, resultNode.desc, wrapWithMaxLocalCalc(resultNode))
|
||||
val remappingMethodAdapter = MethodRemapper(localVariablesSorter, AsmTypeRemapper(remapper, result))
|
||||
|
||||
val fakeContinuationName = CoroutineTransformer.findFakeContinuationConstructorClassName(node)
|
||||
val markerShift = calcMarkerShift(parameters, node)
|
||||
@@ -174,7 +169,9 @@ class MethodInliner(
|
||||
transformationInfo = iterator.next()
|
||||
|
||||
val oldClassName = transformationInfo!!.oldClassName
|
||||
if (transformationInfo!!.shouldRegenerate(isSameModule)) {
|
||||
if (inliningContext.parent?.transformationInfo?.oldClassName == oldClassName) {
|
||||
// Object constructs itself, don't enter an infinite recursion of regeneration.
|
||||
} else if (transformationInfo!!.shouldRegenerate(isSameModule)) {
|
||||
//TODO: need poping of type but what to do with local funs???
|
||||
val newClassName = transformationInfo!!.newClassName
|
||||
remapper.addMapping(oldClassName, newClassName)
|
||||
@@ -206,7 +203,7 @@ class MethodInliner(
|
||||
ReifiedTypeInliner.putNeedClassReificationMarker(mv)
|
||||
result.reifiedTypeParametersUsages.mergeAll(transformResult.reifiedTypeParametersUsages)
|
||||
}
|
||||
} else if (!transformationInfo!!.wasAlreadyRegenerated) {
|
||||
} else {
|
||||
result.addNotChangedClass(oldClassName)
|
||||
}
|
||||
}
|
||||
@@ -300,7 +297,7 @@ class MethodInliner(
|
||||
|
||||
val varRemapper = LocalVarRemapper(lambdaParameters, valueParamShift)
|
||||
//TODO add skipped this and receiver
|
||||
val lambdaResult = inliner.doInline(this.mv, varRemapper, true, info, invokeCall.finallyDepthShift)
|
||||
val lambdaResult = inliner.doInline(localVariablesSorter, varRemapper, true, info, invokeCall.finallyDepthShift)
|
||||
result.mergeWithNotChangeInfo(lambdaResult)
|
||||
result.reifiedTypeParametersUsages.mergeAll(lambdaResult.reifiedTypeParametersUsages)
|
||||
|
||||
@@ -312,19 +309,19 @@ class MethodInliner(
|
||||
inlineOnlySmapSkipper?.onInlineLambdaEnd(remappingMethodAdapter)
|
||||
} else if (isAnonymousConstructorCall(owner, name)) { //TODO add method
|
||||
//TODO add proper message
|
||||
var info = transformationInfo as? AnonymousObjectTransformationInfo ?: throw AssertionError(
|
||||
val newInfo = transformationInfo as? AnonymousObjectTransformationInfo ?: throw AssertionError(
|
||||
"<init> call doesn't correspond to object transformation info for '$owner.$name': $transformationInfo"
|
||||
)
|
||||
val objectConstructsItself = inlineCallSiteInfo.ownerClassName == info.oldClassName
|
||||
if (objectConstructsItself) {
|
||||
// inline fun f() -> new f$1 -> fun something() in class f$1 -> new f$1
|
||||
// ^-- fetch the info that was created for this instruction
|
||||
info = inliningContext.parent?.transformationInfo as? AnonymousObjectTransformationInfo
|
||||
?: throw AssertionError("anonymous object $owner constructs itself, but we have no info on in")
|
||||
}
|
||||
// inline fun f() -> new f$1 -> fun something() in class f$1 -> new f$1
|
||||
// ^-- fetch the info that was created for this instruction
|
||||
// Currently, this self-reference pattern only happens in coroutine objects, which construct
|
||||
// copies of themselves in `create` or `invoke` (not `invokeSuspend`).
|
||||
val existingInfo = inliningContext.parent?.transformationInfo?.takeIf { it.oldClassName == newInfo.oldClassName }
|
||||
as AnonymousObjectTransformationInfo?
|
||||
val info = existingInfo ?: newInfo
|
||||
if (info.shouldRegenerate(isSameModule)) {
|
||||
for (capturedParamDesc in info.allRecapturedParameters) {
|
||||
val realDesc = if (objectConstructsItself && capturedParamDesc.fieldName == AsmUtil.THIS) {
|
||||
val realDesc = if (existingInfo != null && capturedParamDesc.fieldName == AsmUtil.THIS) {
|
||||
// The captures in `info` are relative to the parent context, so a normal `this` there
|
||||
// is a captured outer `this` here.
|
||||
CapturedParamDesc(Type.getObjectType(owner), AsmUtil.CAPTURED_THIS_FIELD, capturedParamDesc.type)
|
||||
|
||||
@@ -26,10 +26,6 @@ interface TransformationInfo {
|
||||
|
||||
val nameGenerator: NameGenerator
|
||||
|
||||
val wasAlreadyRegenerated: Boolean
|
||||
get() = false
|
||||
|
||||
|
||||
fun shouldRegenerate(sameModule: Boolean): Boolean
|
||||
|
||||
fun canRemoveAfterTransformation(): Boolean
|
||||
@@ -86,9 +82,6 @@ class AnonymousObjectTransformationInfo internal constructor(
|
||||
|
||||
lateinit var capturedLambdasToInline: Map<String, LambdaInfo>
|
||||
|
||||
override val wasAlreadyRegenerated: Boolean
|
||||
get() = alreadyRegenerated
|
||||
|
||||
constructor(
|
||||
ownerInternalName: String,
|
||||
needReification: Boolean,
|
||||
@@ -97,9 +90,14 @@ class AnonymousObjectTransformationInfo internal constructor(
|
||||
nameGenerator: NameGenerator
|
||||
) : this(ownerInternalName, needReification, hashMapOf(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator)
|
||||
|
||||
override fun shouldRegenerate(sameModule: Boolean): Boolean = !alreadyRegenerated &&
|
||||
(!sameModule || capturedOuterRegenerated || needReification || capturesAnonymousObjectThatMustBeRegenerated ||
|
||||
functionalArguments.values.any { it != NonInlineableArgumentForInlineableParameterCalledInSuspend })
|
||||
// TODO: unconditionally regenerating an object if it has previously been regenerated is a hack that works around
|
||||
// the fact that TypeRemapper cannot differentiate between different references to the same object. See the test
|
||||
// boxInline/anonymousObject/constructOriginalInRegenerated.kt for an example where a single anonymous object
|
||||
// is referenced twice with otherwise different `shouldRegenerate` results and the inliner gets confused, trying
|
||||
// to map the inner reference to the outer regenerated type and producing an infinite recursion.
|
||||
override fun shouldRegenerate(sameModule: Boolean): Boolean = alreadyRegenerated ||
|
||||
!sameModule || capturedOuterRegenerated || needReification || capturesAnonymousObjectThatMustBeRegenerated ||
|
||||
functionalArguments.values.any { it != NonInlineableArgumentForInlineableParameterCalledInSuspend }
|
||||
|
||||
override fun canRemoveAfterTransformation(): Boolean {
|
||||
// Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC within lambda
|
||||
|
||||
+5
@@ -102,6 +102,11 @@ public class FirBlackBoxInlineCodegenTestGenerated extends AbstractFirBlackBoxIn
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/changingReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructOriginalInRegenerated.kt")
|
||||
public void testConstructOriginalInRegenerated() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructOriginalInRegenerated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructorVisibility.kt")
|
||||
public void testConstructorVisibility() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructorVisibility.kt");
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// FILE: 1.kt
|
||||
// Intentionally in the same package, as objects in other packages are always regenerated.
|
||||
|
||||
fun f(x: () -> String) = x()
|
||||
|
||||
inline fun g(crossinline x: () -> String) = f { x() }
|
||||
|
||||
// FILE: 2.kt
|
||||
// _1Kt$g$1 not regenerated because the original already does the same thing (invoking a functional object)
|
||||
// \-v
|
||||
fun h(x: () -> String) = g { g(x) }
|
||||
// /-^
|
||||
// _1Kt$g$1 regenerated to inline the lambda
|
||||
|
||||
fun box() = h { "OK" }
|
||||
+5
@@ -102,6 +102,11 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/changingReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructOriginalInRegenerated.kt")
|
||||
public void testConstructOriginalInRegenerated() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructOriginalInRegenerated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructorVisibility.kt")
|
||||
public void testConstructorVisibility() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructorVisibility.kt");
|
||||
|
||||
Generated
+5
@@ -102,6 +102,11 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/changingReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructOriginalInRegenerated.kt")
|
||||
public void testConstructOriginalInRegenerated() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructOriginalInRegenerated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructorVisibility.kt")
|
||||
public void testConstructorVisibility() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructorVisibility.kt");
|
||||
|
||||
Generated
+5
@@ -102,6 +102,11 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/changingReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructOriginalInRegenerated.kt")
|
||||
public void testConstructOriginalInRegenerated() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructOriginalInRegenerated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructorVisibility.kt")
|
||||
public void testConstructorVisibility() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructorVisibility.kt");
|
||||
|
||||
+5
@@ -102,6 +102,11 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/changingReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructOriginalInRegenerated.kt")
|
||||
public void testConstructOriginalInRegenerated() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructOriginalInRegenerated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructorVisibility.kt")
|
||||
public void testConstructorVisibility() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructorVisibility.kt");
|
||||
|
||||
Generated
+5
@@ -102,6 +102,11 @@ public class JvmIrAgainstOldBoxInlineTestGenerated extends AbstractJvmIrAgainstO
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/changingReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructOriginalInRegenerated.kt")
|
||||
public void testConstructOriginalInRegenerated() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructOriginalInRegenerated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructorVisibility.kt")
|
||||
public void testConstructorVisibility() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructorVisibility.kt");
|
||||
|
||||
Generated
+5
@@ -102,6 +102,11 @@ public class JvmOldAgainstIrBoxInlineTestGenerated extends AbstractJvmOldAgainst
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/changingReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructOriginalInRegenerated.kt")
|
||||
public void testConstructOriginalInRegenerated() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructOriginalInRegenerated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructorVisibility.kt")
|
||||
public void testConstructorVisibility() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructorVisibility.kt");
|
||||
|
||||
+5
@@ -102,6 +102,11 @@ public class IrJsCodegenInlineES6TestGenerated extends AbstractIrJsCodegenInline
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/changingReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructOriginalInRegenerated.kt")
|
||||
public void testConstructOriginalInRegenerated() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructOriginalInRegenerated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructorVisibility.kt")
|
||||
public void testConstructorVisibility() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructorVisibility.kt");
|
||||
|
||||
Generated
+5
@@ -102,6 +102,11 @@ public class IrJsCodegenInlineTestGenerated extends AbstractIrJsCodegenInlineTes
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/changingReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructOriginalInRegenerated.kt")
|
||||
public void testConstructOriginalInRegenerated() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructOriginalInRegenerated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructorVisibility.kt")
|
||||
public void testConstructorVisibility() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructorVisibility.kt");
|
||||
|
||||
Generated
+5
@@ -102,6 +102,11 @@ public class JsCodegenInlineTestGenerated extends AbstractJsCodegenInlineTest {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/changingReturnType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructOriginalInRegenerated.kt")
|
||||
public void testConstructOriginalInRegenerated() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructOriginalInRegenerated.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructorVisibility.kt")
|
||||
public void testConstructorVisibility() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/constructorVisibility.kt");
|
||||
|
||||
Reference in New Issue
Block a user