Generate local variable table for inlined lambda

This commit is contained in:
Mikhael Bogdanov
2014-03-19 17:45:46 +04:00
parent 75c57f9827
commit 93367ffd3b
9 changed files with 162 additions and 31 deletions
@@ -229,28 +229,20 @@ public class MethodInliner {
node.instructions.resetLabels(); node.instructions.resetLabels();
MethodNode transformedNode = new MethodNode(node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null) { 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 @Override
public void visitVarInsn(int opcode, int var) { public void visitVarInsn(int opcode, int var) {
int newIndex; super.visitVarInsn(opcode, getNewIndex(var));
if (var < realParametersSize) {
newIndex = var;
} else {
newIndex = var + capturedParamsSize;
}
super.visitVarInsn(opcode, newIndex);
} }
@Override @Override
public void visitIincInsn(int var, int increment) { public void visitIincInsn(int var, int increment) {
int newIndex; super.visitIincInsn(getNewIndex(var), increment);
if (var < realParametersSize) {
newIndex = var;
} else {
newIndex = var + capturedParamsSize;
}
super.visitIincInsn(newIndex, increment);
} }
@Override @Override
@@ -260,10 +252,19 @@ public class MethodInliner {
@Override @Override
public void visitLineNumber(int line, Label start) { public void visitLineNumber(int line, Label start) {
if(keepLineNumbers) { if(isInliningLambda) {
super.visitLineNumber(line, start); 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); node.accept(transformedNode);
@@ -34,6 +34,8 @@ public class RemapVisitor extends InstructionAdapter {
private final FieldRemapper nodeRemapper; private final FieldRemapper nodeRemapper;
private final InstructionAdapter instructionAdapter;
protected RemapVisitor( protected RemapVisitor(
MethodVisitor mv, MethodVisitor mv,
Label end, Label end,
@@ -42,6 +44,7 @@ public class RemapVisitor extends InstructionAdapter {
FieldRemapper nodeRemapper FieldRemapper nodeRemapper
) { ) {
super(InlineCodegenUtil.API, mv); super(InlineCodegenUtil.API, mv);
this.instructionAdapter = new InstructionAdapter(mv);
this.end = end; this.end = end;
this.remapper = varRemapper; this.remapper = varRemapper;
this.remapReturn = remapReturn; this.remapReturn = remapReturn;
@@ -65,7 +68,14 @@ public class RemapVisitor extends InstructionAdapter {
@Override @Override
public void visitVarInsn(int opcode, int var) { 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 @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 @Override
public AnnotationVisitor visitAnnotationDefault() { public AnnotationVisitor visitAnnotationDefault() {
return null; return null;
@@ -18,12 +18,15 @@ package org.jetbrains.jet.codegen.inline;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.Label;
import org.jetbrains.asm4.MethodVisitor; import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes; import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.commons.InstructionAdapter; import org.jetbrains.asm4.commons.InstructionAdapter;
import org.jetbrains.jet.codegen.StackValue; import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants; import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
import static org.jetbrains.jet.codegen.inline.VarRemapper.RemapStatus.*;
public class VarRemapper { public class VarRemapper {
private final int allParamsSize; private final int allParamsSize;
@@ -63,10 +66,10 @@ public class VarRemapper {
ParameterInfo info = params.get(index); ParameterInfo info = params.get(index);
StackValue remapped = remapValues[index]; StackValue remapped = remapValues[index];
if (info.isSkipped || remapped == null) { if (info.isSkipped || remapped == null) {
throw new RuntimeException("Trying to access skipped parameter: " + info.type + " at " +index); return new RemapInfo(info);
} }
if (info.isRemapped()) { if (info.isRemapped()) {
return new RemapInfo(remapped, info); return new RemapInfo(remapped, info, REMAPPED);
} else { } else {
remappedIndex = ((StackValue.Local)remapped).index; 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 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) { 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) { public void visitIincInsn(int var, int increment, MethodVisitor mv) {
@@ -87,6 +95,15 @@ public class VarRemapper {
mv.visitIincInsn(((StackValue.Local) remap.value).index, increment); 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) { public void visitVarInsn(int opcode, int var, InstructionAdapter mv) {
RemapInfo remapInfo = remap(var); RemapInfo remapInfo = remap(var);
StackValue value = remapInfo.value; 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 StackValue value;
public final ParameterInfo parameterInfo; 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; this.value = value;
parameterInfo = info; parameterInfo = info;
this.status = remapStatus;
}
public RemapInfo(@NotNull ParameterInfo info) {
this.value = null;
parameterInfo = info;
this.status = FAIL;
} }
} }
} }
@@ -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
@@ -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
@@ -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
@@ -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
@@ -137,7 +137,7 @@ public abstract class AbstractCheckLocalVariablesTableTest extends TestCaseWithT
private List<LocalVariable> parseExpectations() throws IOException { private List<LocalVariable> parseExpectations() throws IOException {
List<String> lines = Files.readLines(ktFile, Charset.forName("utf-8")); List<String> lines = Files.readLines(ktFile, Charset.forName("utf-8"));
List<LocalVariable> expectedLocalVariables = new ArrayList<LocalVariable>(); List<LocalVariable> expectedLocalVariables = new ArrayList<LocalVariable>();
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)); Matcher nameMatcher = namePattern.matcher(lines.get(i));
if (nameMatcher.matches()) { if (nameMatcher.matches()) {
Matcher typeMatcher = typePattern.matcher(lines.get(i)); Matcher typeMatcher = typePattern.matcher(lines.get(i));
@@ -41,6 +41,26 @@ public class CheckLocalVariablesTableTestGenerated extends AbstractCheckLocalVar
doTest("compiler/testData/checkLocalVariablesTable/copyFunction.kt"); 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") @TestMetadata("itInLambda.kt")
public void testItInLambda() throws Exception { public void testItInLambda() throws Exception {
doTest("compiler/testData/checkLocalVariablesTable/itInLambda.kt"); doTest("compiler/testData/checkLocalVariablesTable/itInLambda.kt");