Fix for KT-7273: Bad local variable type when using non-local return
#KT-7273 Fixed
This commit is contained in:
+18
-8
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -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
|
||||
}
|
||||
}
|
||||
+6
@@ -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)
|
||||
|
||||
+6
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user