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 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+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;
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
+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);
|
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)
|
||||||
|
|||||||
+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);
|
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)
|
||||||
|
|||||||
Reference in New Issue
Block a user