Fix for KT-7273: Bad local variable type when using non-local return

#KT-7273 Fixed
This commit is contained in:
Michael Bogdanov
2015-04-15 14:07:10 +03:00
parent 51d5b52f69
commit 0080ebd386
11 changed files with 80 additions and 20 deletions
@@ -19,15 +19,19 @@ package org.jetbrains.kotlin.codegen.inline
import com.google.common.collect.LinkedListMultimap import com.google.common.collect.LinkedListMultimap
import java.util.ArrayList import java.util.ArrayList
import com.intellij.util.containers.Stack import com.intellij.util.containers.Stack
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.*
import java.util.Comparator import java.util.Comparator
import java.util.Collections import java.util.Collections
public abstract class CoveringTryCatchNodeProcessor { public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
public val tryBlocksMetaInfo: IntervalMetaInfo<TryCatchBlockNodeInfo> = IntervalMetaInfo(); public val tryBlocksMetaInfo: IntervalMetaInfo<TryCatchBlockNodeInfo> = IntervalMetaInfo()
public val localVarsMetaInfo: IntervalMetaInfo<LocalVarNodeWrapper> = IntervalMetaInfo(); public val localVarsMetaInfo: IntervalMetaInfo<LocalVarNodeWrapper> = IntervalMetaInfo()
public var nextFreeLocalIndex: Int = parameterSize
private set
public val coveringFromInnermost: List<TryCatchBlockNodeInfo> public val coveringFromInnermost: List<TryCatchBlockNodeInfo>
get() = tryBlocksMetaInfo.currentIntervals.reverse() get() = tryBlocksMetaInfo.currentIntervals.reverse()
@@ -41,10 +45,16 @@ public abstract class CoveringTryCatchNodeProcessor {
} }
public open fun processInstruction(curInstr: AbstractInsnNode, directOrder: Boolean) { public open fun processInstruction(curInstr: AbstractInsnNode, directOrder: Boolean) {
if (curInstr !is LabelNode) return if (curInstr is VarInsnNode || curInstr is IincInsnNode) {
val argSize = InlineCodegenUtil.getLoadStoreArgSize(curInstr.getOpcode())
val varIndex = if (curInstr is VarInsnNode) curInstr.`var` else (curInstr as IincInsnNode).`var`
nextFreeLocalIndex = Math.max(nextFreeLocalIndex, varIndex + argSize)
}
updateCoveringTryBlocks(curInstr, directOrder) if (curInstr is LabelNode) {
updateCoveringLocalVars(curInstr, directOrder) updateCoveringTryBlocks(curInstr, directOrder)
updateCoveringLocalVars(curInstr, directOrder)
}
} }
//Keep information about try blocks that cover current instruction - //Keep information about try blocks that cover current instruction -
@@ -164,11 +174,11 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
while (end != start && end is LabelNode) { while (end != start && end is LabelNode) {
end = end.getPrevious() end = end.getPrevious()
} }
return start == end; return start == end
} }
} }
public class DefaultProcessor(val node: MethodNode) : CoveringTryCatchNodeProcessor() { public class DefaultProcessor(val node: MethodNode, parameterSize: Int) : CoveringTryCatchNodeProcessor(parameterSize) {
init { init {
node.tryCatchBlocks.forEach { addTryNode(it) } node.tryCatchBlocks.forEach { addTryNode(it) }
@@ -25,6 +25,8 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getLoadStoreArgSize;
public class InlineAdapter extends InstructionAdapter { public class InlineAdapter extends InstructionAdapter {
private int nextLocalIndex = 0; private int nextLocalIndex = 0;
@@ -51,7 +53,7 @@ public class InlineAdapter extends InstructionAdapter {
@Override @Override
public void visitVarInsn(int opcode, int var) { public void visitVarInsn(int opcode, int var) {
super.visitVarInsn(opcode, var); super.visitVarInsn(opcode, var);
updateIndex(var, (opcode == Opcodes.DSTORE || opcode == Opcodes.LSTORE || opcode == Opcodes.DLOAD || opcode == Opcodes.LLOAD ? 2 : 1)); updateIndex(var, getLoadStoreArgSize(opcode));
} }
private void updateIndex(int var, int varSize) { private void updateIndex(int var, int varSize) {
@@ -53,7 +53,6 @@ import org.jetbrains.org.objectweb.asm.commons.Method;
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode; import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
import org.jetbrains.org.objectweb.asm.tree.LabelNode; import org.jetbrains.org.objectweb.asm.tree.LabelNode;
import org.jetbrains.org.objectweb.asm.tree.MethodNode; import org.jetbrains.org.objectweb.asm.tree.MethodNode;
import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
@@ -287,7 +286,7 @@ public class InlineCodegen extends CallGenerator {
} }
}; };
List<MethodInliner.PointForExternalFinallyBlocks> infos = MethodInliner.processReturns(adapter, labelOwner, true, null); List<MethodInliner.PointForExternalFinallyBlocks> infos = MethodInliner.processReturns(adapter, labelOwner, true, null);
generateAndInsertFinallyBlocks(adapter, infos); generateAndInsertFinallyBlocks(adapter, infos, ((StackValue.Local)remapper.remap(parameters.totalSize() + 1).value).index);
adapter.accept(new InliningInstructionAdapter(codegen.v)); adapter.accept(new InliningInstructionAdapter(codegen.v));
@@ -624,7 +623,11 @@ public class InlineCodegen extends CallGenerator {
} }
public void generateAndInsertFinallyBlocks(MethodNode intoNode, List<MethodInliner.PointForExternalFinallyBlocks> insertPoints) { public void generateAndInsertFinallyBlocks(
MethodNode intoNode,
List<MethodInliner.PointForExternalFinallyBlocks> insertPoints,
int offsetForFinallyLocalVar
) {
if (!codegen.hasFinallyBlocks()) return; if (!codegen.hasFinallyBlocks()) return;
Map<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks> extensionPoints = Map<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks> extensionPoints =
@@ -633,7 +636,7 @@ public class InlineCodegen extends CallGenerator {
extensionPoints.put(insertPoint.beforeIns, insertPoint); extensionPoints.put(insertPoint.beforeIns, insertPoint);
} }
DefaultProcessor processor = new DefaultProcessor(intoNode); DefaultProcessor processor = new DefaultProcessor(intoNode, offsetForFinallyLocalVar);
AbstractInsnNode curInstr = intoNode.instructions.getFirst(); AbstractInsnNode curInstr = intoNode.instructions.getFirst();
while (curInstr != null) { while (curInstr != null) {
@@ -652,6 +655,12 @@ public class InlineCodegen extends CallGenerator {
codegen.getContext(), codegen.getState(), codegen.getParentCodegen()); codegen.getContext(), codegen.getState(), codegen.getParentCodegen());
finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements()); finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements());
FrameMap frameMap = finallyCodegen.getFrameMap();
FrameMap.Mark mark = frameMap.mark();
while (frameMap.getCurrentSize() < processor.getNextFreeLocalIndex()) {
frameMap.enterTemp(Type.INT_TYPE);
}
finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType); finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType);
finallyNode.visitLabel(end); finallyNode.visitLabel(end);
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method //Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
@@ -661,6 +670,8 @@ public class InlineCodegen extends CallGenerator {
processor.getTryBlocksMetaInfo().splitCurrentIntervals(splitBy, false); processor.getTryBlocksMetaInfo().splitCurrentIntervals(splitBy, false);
processor.getLocalVarsMetaInfo().splitCurrentIntervals(splitBy, false); processor.getLocalVarsMetaInfo().splitCurrentIntervals(splitBy, false);
mark.dropTo();
} }
curInstr = curInstr.getNext(); curInstr = curInstr.getNext();
@@ -675,4 +686,5 @@ public class InlineCodegen extends CallGenerator {
private SourceMapper createNestedSourceMapper(@NotNull SMAPAndMethodNode nodeAndSmap) { private SourceMapper createNestedSourceMapper(@NotNull SMAPAndMethodNode nodeAndSmap) {
return new NestedSourceMapper(sourceMapper, nodeAndSmap.getRanges(), nodeAndSmap.getClassSMAP().getSourceInfo()); return new NestedSourceMapper(sourceMapper, nodeAndSmap.getRanges(), nodeAndSmap.getClassSMAP().getSourceInfo());
} }
} }
@@ -455,4 +455,8 @@ public class InlineCodegenUtil {
(isStartNotEnd ? INLINE_MARKER_BEFORE_METHOD_NAME : INLINE_MARKER_AFTER_METHOD_NAME), (isStartNotEnd ? INLINE_MARKER_BEFORE_METHOD_NAME : INLINE_MARKER_AFTER_METHOD_NAME),
"()V", false); "()V", false);
} }
public static int getLoadStoreArgSize(int opcode) {
return opcode == Opcodes.DSTORE || opcode == Opcodes.LSTORE || opcode == Opcodes.DLOAD || opcode == Opcodes.LLOAD ? 2 : 1;
}
} }
@@ -62,7 +62,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
} }
} }
public static void processInlineFunFinallyBlocks(@NotNull MethodNode inlineFun, int lambdaTryCatchBlockNodes) { public static void processInlineFunFinallyBlocks(@NotNull MethodNode inlineFun, int lambdaTryCatchBlockNodes, int finallyParamOffset) {
int index = 0; int index = 0;
List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo = new ArrayList<TryCatchBlockNodeInfo>(); List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo = new ArrayList<TryCatchBlockNodeInfo>();
for (TryCatchBlockNode block : inlineFun.tryCatchBlocks) { for (TryCatchBlockNode block : inlineFun.tryCatchBlocks) {
@@ -75,7 +75,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
} }
if (hasFinallyBlocks(inlineFunTryBlockInfo)) { if (hasFinallyBlocks(inlineFunTryBlockInfo)) {
new InternalFinallyBlockInliner(inlineFun, inlineFunTryBlockInfo, localVars).processInlineFunFinallyBlocks(); new InternalFinallyBlockInliner(inlineFun, inlineFunTryBlockInfo, localVars, finallyParamOffset).processInlineFunFinallyBlocks();
} }
} }
@@ -85,7 +85,11 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
//lambdaTryCatchBlockNodes is number of TryCatchBlockNodes that was inlined with lambdas into function //lambdaTryCatchBlockNodes is number of TryCatchBlockNodes that was inlined with lambdas into function
//due to code generation specific they placed before function TryCatchBlockNodes //due to code generation specific they placed before function TryCatchBlockNodes
private InternalFinallyBlockInliner(@NotNull MethodNode inlineFun, List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo, List<LocalVarNodeWrapper> localVariableInfo) { private InternalFinallyBlockInliner(@NotNull MethodNode inlineFun,
@NotNull List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo,
@NotNull List<LocalVarNodeWrapper> localVariableInfo,
int finallyParamOffset) {
super(finallyParamOffset);
this.inlineFun = inlineFun; this.inlineFun = inlineFun;
for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) { for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) {
getTryBlocksMetaInfo().addNewInterval(block); getTryBlocksMetaInfo().addNewInterval(block);
@@ -97,8 +101,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
} }
private int initAndGetVarIndexForNonLocalReturnValue() { private int initAndGetVarIndexForNonLocalReturnValue() {
//sortTryCatchBlocks();/*TODO maybe remove*/
MaxLocalsCalculator tempCalcNode = new MaxLocalsCalculator( MaxLocalsCalculator tempCalcNode = new MaxLocalsCalculator(
InlineCodegenUtil.API, InlineCodegenUtil.API,
inlineFun.access, inlineFun.desc, null inlineFun.access, inlineFun.desc, null
@@ -132,7 +132,7 @@ public class LocalVarRemapper {
FAIL FAIL
} }
private static class RemapInfo { public static class RemapInfo {
public final StackValue value; public final StackValue value;
public final ParameterInfo parameterInfo; public final ParameterInfo parameterInfo;
public final RemapStatus status; public final RemapStatus status;
@@ -128,7 +128,7 @@ public class MethodInliner {
resultNode.visitLabel(end); resultNode.visitLabel(end);
if (inliningContext.isRoot()) { if (inliningContext.isRoot()) {
InternalFinallyBlockInliner.processInlineFunFinallyBlocks(resultNode, lambdasFinallyBlocks); InternalFinallyBlockInliner.processInlineFunFinallyBlocks(resultNode, lambdasFinallyBlocks, ((StackValue.Local)remapper.remap(parameters.totalSize() + 1).value).index);
} }
processReturns(resultNode, labelOwner, remapReturn, end); processReturns(resultNode, labelOwner, remapReturn, end);
@@ -0,0 +1,10 @@
fun box(): String {
try {
test {
return@box "OK"
}
} finally {
}
return "fail"
}
@@ -0,0 +1,8 @@
inline fun <R> test(s: () -> R): R {
var b = false
try {
return s()
} finally {
!b
}
}
@@ -477,6 +477,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally"), Pattern.compile("^(.+)\\.1.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally"), Pattern.compile("^(.+)\\.1.kt$"), true);
} }
@TestMetadata("kt7273.1.kt")
public void testKt7273() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/kt7273.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite") @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -477,6 +477,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally"), Pattern.compile("^(.+)\\.1.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally"), Pattern.compile("^(.+)\\.1.kt$"), true);
} }
@TestMetadata("kt7273.1.kt")
public void testKt7273() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/kt7273.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite") @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)