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:
Denis Zharkov
2017-02-16 10:55:07 +03:00
parent dfb60ba4ac
commit e16b0524b6
7 changed files with 137 additions and 5 deletions
@@ -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