diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java index 7e433d71aae..24e2647d4a1 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java @@ -213,7 +213,7 @@ public class InlineCodegen implements ParentCodegenAware, CallGenerator { codegen.getInlineNameGenerator().subGenerator(functionDescriptor.getName().asString()), codegen.getContext(), call, Collections.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); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaFieldRemapper.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaFieldRemapper.java index 1d8dc5480f4..2fc0329dc23 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaFieldRemapper.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaFieldRemapper.java @@ -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 captured) { + public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection 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"); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaTransformer.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaTransformer.java index ff75f20e2dc..eb3d93d43df 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaTransformer.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/LambdaTransformer.java @@ -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); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java index 21f4296fefd..95ea4a090fc 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java @@ -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(); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/RegeneratedLambdaFieldRemapper.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/RegeneratedLambdaFieldRemapper.java index b6c104f9efb..bbdbb3e06eb 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/RegeneratedLambdaFieldRemapper.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/RegeneratedLambdaFieldRemapper.java @@ -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 recapturedLambdas + Map 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; + } } diff --git a/compiler/testData/codegen/boxInline/noInlineLambdaChain/1.kt b/compiler/testData/codegen/boxInline/noInlineLambdaChain/1.kt new file mode 100644 index 00000000000..273aa7543c0 --- /dev/null +++ b/compiler/testData/codegen/boxInline/noInlineLambdaChain/1.kt @@ -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" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/noInlineLambdaChain/2.kt b/compiler/testData/codegen/boxInline/noInlineLambdaChain/2.kt new file mode 100644 index 00000000000..dd9a104eeb4 --- /dev/null +++ b/compiler/testData/codegen/boxInline/noInlineLambdaChain/2.kt @@ -0,0 +1,5 @@ +package test + +inline fun inlineFun(arg: T, f: (T) -> Unit) { + f(arg) +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java index 44adb1fd31e..bef611583ce 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java index 36b64c9a17d..6057a880014 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -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");