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 java.util.ArrayList
import com.intellij.util.containers.Stack
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.*
import java.util.Comparator
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>
get() = tryBlocksMetaInfo.currentIntervals.reverse()
@@ -41,10 +45,16 @@ public abstract class CoveringTryCatchNodeProcessor {
}
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)
updateCoveringLocalVars(curInstr, directOrder)
if (curInstr is LabelNode) {
updateCoveringTryBlocks(curInstr, directOrder)
updateCoveringLocalVars(curInstr, directOrder)
}
}
//Keep information about try blocks that cover current instruction -
@@ -164,11 +174,11 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
while (end != start && end is LabelNode) {
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 {
node.tryCatchBlocks.forEach { addTryNode(it) }
@@ -25,6 +25,8 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getLoadStoreArgSize;
public class InlineAdapter extends InstructionAdapter {
private int nextLocalIndex = 0;
@@ -51,7 +53,7 @@ public class InlineAdapter extends InstructionAdapter {
@Override
public void visitVarInsn(int opcode, int 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) {
@@ -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.LabelNode;
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode;
import java.io.IOException;
import java.util.HashMap;
@@ -287,7 +286,7 @@ public class InlineCodegen extends CallGenerator {
}
};
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));
@@ -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;
Map<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks> extensionPoints =
@@ -633,7 +636,7 @@ public class InlineCodegen extends CallGenerator {
extensionPoints.put(insertPoint.beforeIns, insertPoint);
}
DefaultProcessor processor = new DefaultProcessor(intoNode);
DefaultProcessor processor = new DefaultProcessor(intoNode, offsetForFinallyLocalVar);
AbstractInsnNode curInstr = intoNode.instructions.getFirst();
while (curInstr != null) {
@@ -652,6 +655,12 @@ public class InlineCodegen extends CallGenerator {
codegen.getContext(), codegen.getState(), codegen.getParentCodegen());
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);
finallyNode.visitLabel(end);
//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.getLocalVarsMetaInfo().splitCurrentIntervals(splitBy, false);
mark.dropTo();
}
curInstr = curInstr.getNext();
@@ -675,4 +686,5 @@ public class InlineCodegen extends CallGenerator {
private SourceMapper createNestedSourceMapper(@NotNull SMAPAndMethodNode nodeAndSmap) {
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),
"()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;
List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo = new ArrayList<TryCatchBlockNodeInfo>();
for (TryCatchBlockNode block : inlineFun.tryCatchBlocks) {
@@ -75,7 +75,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
}
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
//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;
for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) {
getTryBlocksMetaInfo().addNewInterval(block);
@@ -97,8 +101,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
}
private int initAndGetVarIndexForNonLocalReturnValue() {
//sortTryCatchBlocks();/*TODO maybe remove*/
MaxLocalsCalculator tempCalcNode = new MaxLocalsCalculator(
InlineCodegenUtil.API,
inlineFun.access, inlineFun.desc, null
@@ -132,7 +132,7 @@ public class LocalVarRemapper {
FAIL
}
private static class RemapInfo {
public static class RemapInfo {
public final StackValue value;
public final ParameterInfo parameterInfo;
public final RemapStatus status;
@@ -128,7 +128,7 @@ public class MethodInliner {
resultNode.visitLabel(end);
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);
@@ -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);
}
@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")
@TestDataPath("$PROJECT_ROOT")
@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);
}
@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")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)