Field access chain update on lambda regeneration
This commit is contained in:
@@ -213,7 +213,7 @@ public class InlineCodegen implements ParentCodegenAware, CallGenerator {
|
||||
codegen.getInlineNameGenerator().subGenerator(functionDescriptor.getName().asString()),
|
||||
codegen.getContext(), call, Collections.<String, String>emptyMap(), false, false);
|
||||
|
||||
MethodInliner inliner = new MethodInliner(node, parameters, info, null, new LambdaFieldRemapper(), isSameModule); //with captured
|
||||
MethodInliner inliner = new MethodInliner(node, parameters, info, null, new LambdaFieldRemapper(null, null, parameters), isSameModule); //with captured
|
||||
|
||||
VarRemapper.ParamRemapper remapper = new VarRemapper.ParamRemapper(parameters, initialFrameSize);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.jet.codegen.inline;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
import org.jetbrains.asm4.tree.AbstractInsnNode;
|
||||
@@ -30,6 +31,18 @@ import static org.jetbrains.jet.codegen.inline.MethodInliner.getPreviousNoLabelN
|
||||
|
||||
public class LambdaFieldRemapper {
|
||||
|
||||
private String lambdaInternalName;
|
||||
|
||||
protected LambdaFieldRemapper parent;
|
||||
|
||||
private final Parameters params;
|
||||
|
||||
public LambdaFieldRemapper(@Nullable String lambdaInternalName, @Nullable LambdaFieldRemapper parent, @NotNull Parameters methodParams) {
|
||||
this.lambdaInternalName = lambdaInternalName;
|
||||
this.parent = parent;
|
||||
params = methodParams;
|
||||
}
|
||||
|
||||
public AbstractInsnNode doTransform(MethodNode node, FieldInsnNode fieldInsnNode, CapturedParamInfo capturedField) {
|
||||
AbstractInsnNode loadThis = getPreviousThis(fieldInsnNode);
|
||||
|
||||
@@ -58,12 +71,12 @@ public class LambdaFieldRemapper {
|
||||
}
|
||||
|
||||
|
||||
public boolean canProcess(String owner, String currentLambdaType) {
|
||||
public boolean canProcess(@NotNull String owner, @NotNull String currentLambdaType) {
|
||||
return owner.equals(currentLambdaType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CapturedParamInfo findField(FieldInsnNode fieldInsnNode, Collection<CapturedParamInfo> captured) {
|
||||
public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) {
|
||||
for (CapturedParamInfo valueDescriptor : captured) {
|
||||
if (valueDescriptor.getFieldName().equals(fieldInsnNode.name)) {
|
||||
return valueDescriptor;
|
||||
@@ -71,4 +84,28 @@ public class LambdaFieldRemapper {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public LambdaFieldRemapper getParent() {
|
||||
return parent;
|
||||
}
|
||||
public String getLambdaInternalName() {
|
||||
return lambdaInternalName;
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return parent == null;
|
||||
}
|
||||
|
||||
public boolean shouldPatch(@NotNull FieldInsnNode node) {
|
||||
return !isRoot() && parent.shouldPatch(node);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public AbstractInsnNode patch(@NotNull FieldInsnNode field, @NotNull MethodNode node) {
|
||||
//parent is inlined so we need patch instruction chain
|
||||
if (!isRoot()){
|
||||
return parent.patch(field, node);
|
||||
}
|
||||
throw new IllegalStateException("Should be invoked");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class LambdaTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
public InlineResult doTransform(ConstructorInvocation invocation) {
|
||||
public InlineResult doTransform(ConstructorInvocation invocation, LambdaFieldRemapper parentRemapper) {
|
||||
ClassBuilder classBuilder = createClassBuilder();
|
||||
|
||||
//TODO: public visibility for inline function
|
||||
@@ -129,7 +129,8 @@ public class LambdaTransformer {
|
||||
|
||||
MethodVisitor invokeVisitor = newMethod(classBuilder, invoke);
|
||||
RegeneratedLambdaFieldRemapper
|
||||
remapper = new RegeneratedLambdaFieldRemapper(oldLambdaType.getInternalName(), newLambdaType.getInternalName(), parameters, invocation.getCapturedLambdasToInline());
|
||||
remapper = new RegeneratedLambdaFieldRemapper(oldLambdaType.getInternalName(), newLambdaType.getInternalName(), parameters, invocation.getCapturedLambdasToInline(),
|
||||
parentRemapper);
|
||||
MethodInliner inliner = new MethodInliner(invoke, parameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")), oldLambdaType,
|
||||
remapper, isSameModule);
|
||||
InlineResult result = inliner.doInline(invokeVisitor, new VarRemapper.ParamRemapper(parameters, 0), remapper, false);
|
||||
|
||||
@@ -76,7 +76,7 @@ public class MethodInliner {
|
||||
|
||||
|
||||
public InlineResult doInline(MethodVisitor adapter, VarRemapper.ParamRemapper remapper) {
|
||||
return doInline(adapter, remapper, new LambdaFieldRemapper(), true);
|
||||
return doInline(adapter, remapper, lambdaFieldRemapper, true);
|
||||
}
|
||||
|
||||
public InlineResult doInline(
|
||||
@@ -132,7 +132,7 @@ public class MethodInliner {
|
||||
inliningContext.subInline(inliningContext.nameGenerator, currentTypeMapping).classRegeneration(),
|
||||
isSameModule, newLambdaType);
|
||||
|
||||
InlineResult transformResult = transformer.doTransform(invocation);
|
||||
InlineResult transformResult = transformer.doTransform(invocation, capturedRemapper);
|
||||
result.addAllClassesToRemove(transformResult);
|
||||
|
||||
if (inliningContext.isInliningLambda) {
|
||||
@@ -426,34 +426,9 @@ public class MethodInliner {
|
||||
} else {
|
||||
cur = this.lambdaFieldRemapper.doTransform(node, fieldInsnNode, result);
|
||||
}
|
||||
} else {
|
||||
Type type1 = Type.getType(fieldInsnNode.desc);
|
||||
if (!AsmUtil.isPrimitive(type1)) {
|
||||
String type = type1.getInternalName();
|
||||
if (inliningContext.typeMapping.containsKey(type)) {
|
||||
//TODO value could be null
|
||||
String newTypeOrSkip = inliningContext.typeMapping.get(type);
|
||||
if (newTypeOrSkip != null) {
|
||||
fieldInsnNode.owner = newTypeOrSkip;
|
||||
}
|
||||
else {
|
||||
//generate owner of next instruction
|
||||
AbstractInsnNode previous = fieldInsnNode.getPrevious();
|
||||
AbstractInsnNode nextInstruction = fieldInsnNode.getNext();
|
||||
if (!(nextInstruction instanceof FieldInsnNode)) {
|
||||
throw new IllegalStateException(
|
||||
"Instruction after inlined one should be field access: " + nextInstruction);
|
||||
}
|
||||
if (!(previous instanceof FieldInsnNode)) {
|
||||
throw new IllegalStateException("Instruction before inlined one should be field access: " + previous);
|
||||
}
|
||||
cur = nextInstruction;
|
||||
node.instructions.remove(cur.getPrevious());
|
||||
((FieldInsnNode) cur).owner = Type.getType(((FieldInsnNode) previous).desc).getInternalName();
|
||||
((FieldInsnNode) cur).name = LambdaTransformer.getNewFieldName(((FieldInsnNode) cur).name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (lambdaFieldRemapper.shouldPatch(fieldInsnNode)) {
|
||||
cur = lambdaFieldRemapper.patch(fieldInsnNode, node);
|
||||
}
|
||||
}
|
||||
cur = cur.getNext();
|
||||
|
||||
+40
-1
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.jet.codegen.inline;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
import org.jetbrains.asm4.Type;
|
||||
@@ -41,8 +42,10 @@ public class RegeneratedLambdaFieldRemapper extends LambdaFieldRemapper {
|
||||
String oldOwnerType,
|
||||
String newOwnerType,
|
||||
Parameters parameters,
|
||||
Map<String, LambdaInfo> recapturedLambdas
|
||||
Map<String, LambdaInfo> recapturedLambdas,
|
||||
LambdaFieldRemapper remapper
|
||||
) {
|
||||
super(oldOwnerType, remapper, parameters);
|
||||
this.oldOwnerType = oldOwnerType;
|
||||
this.newOwnerType = newOwnerType;
|
||||
this.parameters = parameters;
|
||||
@@ -113,4 +116,40 @@ public class RegeneratedLambdaFieldRemapper extends LambdaFieldRemapper {
|
||||
return super.findField(fieldInsnNode, captured);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPatch(@NotNull FieldInsnNode node) {
|
||||
//parent is inlined so we need patch instruction chain
|
||||
return shouldPatchByMe(node) || parent.shouldPatch(node);
|
||||
}
|
||||
|
||||
private boolean shouldPatchByMe(@NotNull FieldInsnNode node) {
|
||||
//parent is inlined so we need patch instruction chain
|
||||
//aloading inlined this
|
||||
return parent.isRoot() && node.owner.equals(getLambdaInternalName()) && node.name.equals("this$0");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public AbstractInsnNode patch(@NotNull FieldInsnNode fieldInsnNode, @NotNull MethodNode node) {
|
||||
if (!shouldPatchByMe(fieldInsnNode)) {
|
||||
return parent.patch(fieldInsnNode, node);
|
||||
}
|
||||
//parent is inlined so we need patch instruction chain
|
||||
AbstractInsnNode previous = fieldInsnNode.getPrevious();
|
||||
AbstractInsnNode nextInstruction = fieldInsnNode.getNext();
|
||||
if (!(nextInstruction instanceof FieldInsnNode)) {
|
||||
throw new IllegalStateException(
|
||||
"Instruction after inlined one should be field access: " + nextInstruction);
|
||||
}
|
||||
if (!(previous instanceof FieldInsnNode)) {
|
||||
throw new IllegalStateException("Instruction before inlined one should be field access: " + previous);
|
||||
}
|
||||
FieldInsnNode next = (FieldInsnNode) nextInstruction;
|
||||
node.instructions.remove(next.getPrevious());
|
||||
next.owner = Type.getType(((FieldInsnNode) previous).desc).getInternalName();
|
||||
next.name = node.name.equals("this$0") ? node.name : LambdaTransformer.getNewFieldName(next.name);
|
||||
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import test.*
|
||||
|
||||
fun test1(param: String): String {
|
||||
var result = "fail"
|
||||
inlineFun("1") { c ->
|
||||
{
|
||||
inlineFun("2") { a ->
|
||||
{
|
||||
{
|
||||
result = param + c + a
|
||||
}()
|
||||
}()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
fun test2(param: String): String {
|
||||
var result = "fail"
|
||||
inlineFun("2") { a ->
|
||||
{
|
||||
{
|
||||
result = param + a
|
||||
}()
|
||||
}()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
fun box(): String {
|
||||
if (test1("start") != "start12") return "fail1: ${test1("start")}"
|
||||
if (test2("start") != "start2") return "fail2: ${test2("start")}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
inline fun <T> inlineFun(arg: T, f: (T) -> Unit) {
|
||||
f(arg)
|
||||
}
|
||||
+5
@@ -136,6 +136,11 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxCodegenT
|
||||
doTestMultiFile("compiler/testData/codegen/boxInline/noInline");
|
||||
}
|
||||
|
||||
@TestMetadata("noInlineLambdaChain")
|
||||
public void testNoInlineLambdaChain() throws Exception {
|
||||
doTestMultiFile("compiler/testData/codegen/boxInline/noInlineLambdaChain");
|
||||
}
|
||||
|
||||
@TestMetadata("noInlineLambdaX2")
|
||||
public void testNoInlineLambdaX2() throws Exception {
|
||||
doTestMultiFile("compiler/testData/codegen/boxInline/noInlineLambdaX2");
|
||||
|
||||
+5
@@ -136,6 +136,11 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
doBoxTest("compiler/testData/codegen/boxInline/noInline");
|
||||
}
|
||||
|
||||
@TestMetadata("noInlineLambdaChain")
|
||||
public void testNoInlineLambdaChain() throws Exception {
|
||||
doBoxTest("compiler/testData/codegen/boxInline/noInlineLambdaChain");
|
||||
}
|
||||
|
||||
@TestMetadata("noInlineLambdaX2")
|
||||
public void testNoInlineLambdaX2() throws Exception {
|
||||
doBoxTest("compiler/testData/codegen/boxInline/noInlineLambdaX2");
|
||||
|
||||
Reference in New Issue
Block a user