From 93367ffd3be494547013b9fdb32efdc079721b04 Mon Sep 17 00:00:00 2001 From: Mikhael Bogdanov Date: Wed, 19 Mar 2014 17:45:46 +0400 Subject: [PATCH] Generate local variable table for inlined lambda --- .../jet/codegen/inline/MethodInliner.java | 33 +++++++------- .../jet/codegen/inline/RemapVisitor.java | 19 ++++---- .../jet/codegen/inline/VarRemapper.java | 44 ++++++++++++++++--- .../inlineLambdaWithItParam.kt | 16 +++++++ .../inlineLambdaWithParam.kt | 20 +++++++++ .../checkLocalVariablesTable/inlineSimple.kt | 17 +++++++ .../inlineSimpleChain.kt | 22 ++++++++++ .../AbstractCheckLocalVariablesTableTest.java | 2 +- ...CheckLocalVariablesTableTestGenerated.java | 20 +++++++++ 9 files changed, 162 insertions(+), 31 deletions(-) create mode 100644 compiler/testData/checkLocalVariablesTable/inlineLambdaWithItParam.kt create mode 100644 compiler/testData/checkLocalVariablesTable/inlineLambdaWithParam.kt create mode 100644 compiler/testData/checkLocalVariablesTable/inlineSimple.kt create mode 100644 compiler/testData/checkLocalVariablesTable/inlineSimpleChain.kt 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 9eb3d896036..3b2b658e932 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java @@ -229,28 +229,20 @@ public class MethodInliner { node.instructions.resetLabels(); MethodNode transformedNode = new MethodNode(node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null) { - private final boolean keepLineNumbers = nodeRemapper.isInsideInliningLambda(); + private final boolean isInliningLambda = nodeRemapper.isInsideInliningLambda(); + + private int getNewIndex(int var) { + return var + (var < realParametersSize ? 0 : capturedParamsSize); + } @Override public void visitVarInsn(int opcode, int var) { - int newIndex; - if (var < realParametersSize) { - newIndex = var; - } else { - newIndex = var + capturedParamsSize; - } - super.visitVarInsn(opcode, newIndex); + super.visitVarInsn(opcode, getNewIndex(var)); } @Override public void visitIincInsn(int var, int increment) { - int newIndex; - if (var < realParametersSize) { - newIndex = var; - } else { - newIndex = var + capturedParamsSize; - } - super.visitIincInsn(newIndex, increment); + super.visitIincInsn(getNewIndex(var), increment); } @Override @@ -260,10 +252,19 @@ public class MethodInliner { @Override public void visitLineNumber(int line, Label start) { - if(keepLineNumbers) { + if(isInliningLambda) { super.visitLineNumber(line, start); } } + + @Override + public void visitLocalVariable( + String name, String desc, String signature, Label start, Label end, int index + ) { + if (isInliningLambda) { + super.visitLocalVariable(name, desc, signature, start, end, getNewIndex(index)); + } + } }; node.accept(transformedNode); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/RemapVisitor.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/RemapVisitor.java index ddb3531fdec..286ebfcbb5a 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/RemapVisitor.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/RemapVisitor.java @@ -34,6 +34,8 @@ public class RemapVisitor extends InstructionAdapter { private final FieldRemapper nodeRemapper; + private final InstructionAdapter instructionAdapter; + protected RemapVisitor( MethodVisitor mv, Label end, @@ -42,6 +44,7 @@ public class RemapVisitor extends InstructionAdapter { FieldRemapper nodeRemapper ) { super(InlineCodegenUtil.API, mv); + this.instructionAdapter = new InstructionAdapter(mv); this.end = end; this.remapper = varRemapper; this.remapReturn = remapReturn; @@ -65,7 +68,14 @@ public class RemapVisitor extends InstructionAdapter { @Override public void visitVarInsn(int opcode, int var) { - remapper.visitVarInsn(opcode, var, new InstructionAdapter(mv)); + remapper.visitVarInsn(opcode, var, instructionAdapter); + } + + @Override + public void visitLocalVariable( + String name, String desc, String signature, Label start, Label end, int index + ) { + remapper.visitLocalVariable(name, desc, signature, start, end, index, mv); } @Override @@ -86,13 +96,6 @@ public class RemapVisitor extends InstructionAdapter { } } - @Override - public void visitLocalVariable( - String name, String desc, String signature, Label start, Label end, int index - ) { - - } - @Override public AnnotationVisitor visitAnnotationDefault() { return null; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/VarRemapper.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/VarRemapper.java index 2430e91307b..b561958c53e 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/VarRemapper.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/VarRemapper.java @@ -18,12 +18,15 @@ package org.jetbrains.jet.codegen.inline; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.asm4.Label; import org.jetbrains.asm4.MethodVisitor; import org.jetbrains.asm4.Opcodes; import org.jetbrains.asm4.commons.InstructionAdapter; import org.jetbrains.jet.codegen.StackValue; import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants; +import static org.jetbrains.jet.codegen.inline.VarRemapper.RemapStatus.*; + public class VarRemapper { private final int allParamsSize; @@ -63,10 +66,10 @@ public class VarRemapper { ParameterInfo info = params.get(index); StackValue remapped = remapValues[index]; if (info.isSkipped || remapped == null) { - throw new RuntimeException("Trying to access skipped parameter: " + info.type + " at " +index); + return new RemapInfo(info); } if (info.isRemapped()) { - return new RemapInfo(remapped, info); + return new RemapInfo(remapped, info, REMAPPED); } else { remappedIndex = ((StackValue.Local)remapped).index; } @@ -74,11 +77,16 @@ public class VarRemapper { remappedIndex = actualParamsSize - params.totalSize() + index; //captured params not used directly in this inlined method, they used in closure } - return new RemapInfo(StackValue.local(remappedIndex + additionalShift, AsmTypeConstants.OBJECT_TYPE), null); + return new RemapInfo(StackValue.local(remappedIndex + additionalShift, AsmTypeConstants.OBJECT_TYPE), null, SHIFT); } public RemapInfo remap(int index) { - return doRemap(index); + RemapInfo info = doRemap(index); + if (FAIL == info.status) { + assert info.parameterInfo != null : "Parameter info should be not null"; + throw new RuntimeException("Trying to access skipped parameter: " + info.parameterInfo.type + " at " +index); + } + return info; } public void visitIincInsn(int var, int increment, MethodVisitor mv) { @@ -87,6 +95,15 @@ public class VarRemapper { mv.visitIincInsn(((StackValue.Local) remap.value).index, increment); } + public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index, MethodVisitor mv) { + RemapInfo info = doRemap(index); + if (SHIFT == info.status) { + //add entries only for shifted vars + mv.visitLocalVariable(name, desc, signature, start, end, ((StackValue.Local) info.value).index); + } + } + + public void visitVarInsn(int opcode, int var, InstructionAdapter mv) { RemapInfo remapInfo = remap(var); StackValue value = remapInfo.value; @@ -104,15 +121,30 @@ public class VarRemapper { } } - public static class RemapInfo { + public enum RemapStatus { + SHIFT, + REMAPPED, + FAIL + } + + private static class RemapInfo { public final StackValue value; public final ParameterInfo parameterInfo; - public RemapInfo(@NotNull StackValue value, @Nullable ParameterInfo info) { + public final RemapStatus status; + + public RemapInfo(@NotNull StackValue value, @Nullable ParameterInfo info, RemapStatus remapStatus) { this.value = value; parameterInfo = info; + this.status = remapStatus; + } + + public RemapInfo(@NotNull ParameterInfo info) { + this.value = null; + parameterInfo = info; + this.status = FAIL; } } } diff --git a/compiler/testData/checkLocalVariablesTable/inlineLambdaWithItParam.kt b/compiler/testData/checkLocalVariablesTable/inlineLambdaWithItParam.kt new file mode 100644 index 00000000000..3363a458b7a --- /dev/null +++ b/compiler/testData/checkLocalVariablesTable/inlineLambdaWithItParam.kt @@ -0,0 +1,16 @@ +class A { + inline fun inlineFun(s: (s: Int) -> Unit) { + s(11) + } + + fun foo() { + inlineFun ({ + var zzz = it; + }) + } +} + +// METHOD : A.foo()V +// VARIABLE : NAME=zzz TYPE=I INDEX=3 +// VARIABLE : NAME=it TYPE=I INDEX=2 +// VARIABLE : NAME=this TYPE=LA; INDEX=0 diff --git a/compiler/testData/checkLocalVariablesTable/inlineLambdaWithParam.kt b/compiler/testData/checkLocalVariablesTable/inlineLambdaWithParam.kt new file mode 100644 index 00000000000..1923bdb064d --- /dev/null +++ b/compiler/testData/checkLocalVariablesTable/inlineLambdaWithParam.kt @@ -0,0 +1,20 @@ +class A { + inline fun inlineFun(s: (s: Int) -> Unit, p : Int) { + s(11) + + s(p) + } + + fun foo() { + inlineFun ({ l -> + var zzz = l; + }, 11) + } +} + +// METHOD : A.foo()V +// VARIABLE : NAME=zzz TYPE=I INDEX=4 +// VARIABLE : NAME=l TYPE=I INDEX=3 +// VARIABLE : NAME=zzz TYPE=I INDEX=4 +// VARIABLE : NAME=l TYPE=I INDEX=3 +// VARIABLE : NAME=this TYPE=LA; INDEX=0 diff --git a/compiler/testData/checkLocalVariablesTable/inlineSimple.kt b/compiler/testData/checkLocalVariablesTable/inlineSimple.kt new file mode 100644 index 00000000000..472a8771b2e --- /dev/null +++ b/compiler/testData/checkLocalVariablesTable/inlineSimple.kt @@ -0,0 +1,17 @@ +class A { + inline fun inlineFun(s: () -> Unit) { + s() + } + + fun foo() { + var s = 1; + inlineFun ({ + var zzz = 2; + }) + } +} + +// METHOD : A.foo()V +// VARIABLE : NAME=zzz TYPE=I INDEX=3 +// VARIABLE : NAME=s TYPE=I INDEX=1 +// VARIABLE : NAME=this TYPE=LA; INDEX=0 diff --git a/compiler/testData/checkLocalVariablesTable/inlineSimpleChain.kt b/compiler/testData/checkLocalVariablesTable/inlineSimpleChain.kt new file mode 100644 index 00000000000..94d4432cea4 --- /dev/null +++ b/compiler/testData/checkLocalVariablesTable/inlineSimpleChain.kt @@ -0,0 +1,22 @@ +class A { + inline fun inlineFun(s: () -> Unit) { + s() + } + + fun foo() { + var s = 0; + inlineFun { + var z = 1; + + inlineFun { + var zz2 = 2; + } + } + } +} + +// METHOD : A.foo()V +// VARIABLE : NAME=zz2 TYPE=I INDEX=5 +// VARIABLE : NAME=z TYPE=I INDEX=3 +// VARIABLE : NAME=s TYPE=I INDEX=1 +// VARIABLE : NAME=this TYPE=LA; INDEX=0 diff --git a/compiler/tests/org/jetbrains/jet/codegen/AbstractCheckLocalVariablesTableTest.java b/compiler/tests/org/jetbrains/jet/codegen/AbstractCheckLocalVariablesTableTest.java index 2df6fdb9104..3bd99b58461 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/AbstractCheckLocalVariablesTableTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/AbstractCheckLocalVariablesTableTest.java @@ -137,7 +137,7 @@ public abstract class AbstractCheckLocalVariablesTableTest extends TestCaseWithT private List parseExpectations() throws IOException { List lines = Files.readLines(ktFile, Charset.forName("utf-8")); List expectedLocalVariables = new ArrayList(); - for (int i = lines.size() - 3; i < lines.size(); ++i) { + for (int i = Math.max(lines.size() - 10, 0); i < lines.size(); ++i) { Matcher nameMatcher = namePattern.matcher(lines.get(i)); if (nameMatcher.matches()) { Matcher typeMatcher = typePattern.matcher(lines.get(i)); diff --git a/compiler/tests/org/jetbrains/jet/codegen/CheckLocalVariablesTableTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/CheckLocalVariablesTableTestGenerated.java index e7e45b84be4..dbbbb0b758f 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/CheckLocalVariablesTableTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/CheckLocalVariablesTableTestGenerated.java @@ -41,6 +41,26 @@ public class CheckLocalVariablesTableTestGenerated extends AbstractCheckLocalVar doTest("compiler/testData/checkLocalVariablesTable/copyFunction.kt"); } + @TestMetadata("inlineLambdaWithItParam.kt") + public void testInlineLambdaWithItParam() throws Exception { + doTest("compiler/testData/checkLocalVariablesTable/inlineLambdaWithItParam.kt"); + } + + @TestMetadata("inlineLambdaWithParam.kt") + public void testInlineLambdaWithParam() throws Exception { + doTest("compiler/testData/checkLocalVariablesTable/inlineLambdaWithParam.kt"); + } + + @TestMetadata("inlineSimple.kt") + public void testInlineSimple() throws Exception { + doTest("compiler/testData/checkLocalVariablesTable/inlineSimple.kt"); + } + + @TestMetadata("inlineSimpleChain.kt") + public void testInlineSimpleChain() throws Exception { + doTest("compiler/testData/checkLocalVariablesTable/inlineSimpleChain.kt"); + } + @TestMetadata("itInLambda.kt") public void testItInLambda() throws Exception { doTest("compiler/testData/checkLocalVariablesTable/itInLambda.kt");