Field access chain update on lambda regeneration

This commit is contained in:
Mikhael Bogdanov
2014-03-07 14:30:10 +04:00
parent 632061b324
commit e54ece8d12
9 changed files with 143 additions and 36 deletions
@@ -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();
@@ -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)
}
@@ -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");
@@ -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");