Updated exception table generation: support new exception table in non-local returns

This commit is contained in:
Michael Bogdanov
2015-06-10 15:38:09 +03:00
parent 93c5a52d13
commit a018e4e12a
40 changed files with 1586 additions and 138 deletions
@@ -129,6 +129,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
private int myLastLineNumber = -1;
private boolean shouldMarkLineNumbers = true;
private int finallyDeep = 0;
public ExpressionCodegen(
@NotNull MethodVisitor mv,
@@ -1759,7 +1760,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
}
}
private void doFinallyOnReturn(Label afterReturnLabel) {
private void doFinallyOnReturn(@NotNull Label afterReturnLabel) {
if(!blockStackElements.isEmpty()) {
BlockStackElement stackElement = blockStackElements.peek();
if (stackElement instanceof FinallyBlockStackElement) {
@@ -1790,9 +1791,10 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
private void genFinallyBlockOrGoto(
@Nullable FinallyBlockStackElement finallyBlockStackElement,
@Nullable Label tryCatchBlockEnd,
@Nullable Label afterReturnLabel
@Nullable Label afterJumpLabel
) {
if (finallyBlockStackElement != null) {
finallyDeep++;
assert finallyBlockStackElement.gaps.size() % 2 == 0 : "Finally block gaps are inconsistent";
BlockStackElement topOfStack = blockStackElements.pop();
@@ -1802,21 +1804,25 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
Label finallyStart = new Label();
v.mark(finallyStart);
finallyBlockStackElement.addGapLabel(finallyStart);
if (context.isInlineFunction() || context.isInliningLambda()) {
InlineCodegenUtil.generateFinallyMarker(v, finallyDeep, true);
}
//noinspection ConstantConditions
gen(jetTryExpression.getFinallyBlock().getFinalExpression(), Type.VOID_TYPE);
if (context.isInlineFunction() || context.isInliningLambda()) {
InlineCodegenUtil.generateFinallyMarker(v, finallyDeep, false);
}
}
if (tryCatchBlockEnd != null) {
if (context.isInlineFunction()) {
InlineCodegenUtil.generateGoToTryCatchBlockEndMarker(v);
}
v.goTo(tryCatchBlockEnd);
}
if (finallyBlockStackElement != null) {
Label finallyEnd = afterReturnLabel != null ? afterReturnLabel : new Label();
if (afterReturnLabel == null) {
finallyDeep--;
Label finallyEnd = afterJumpLabel != null ? afterJumpLabel : new Label();
if (afterJumpLabel == null) {
v.mark(finallyEnd);
}
finallyBlockStackElement.addGapLabel(finallyEnd);
@@ -4000,8 +4006,9 @@ The "returned" value of try expression with no finally is either the last expres
return new Stack<BlockStackElement>(blockStackElements);
}
public void addBlockStackElementsForNonLocalReturns(@NotNull Stack<BlockStackElement> elements) {
public void addBlockStackElementsForNonLocalReturns(@NotNull Stack<BlockStackElement> elements, int finallyDeepIndex) {
blockStackElements.addAll(elements);
this.finallyDeep = finallyDeepIndex;
}
private static class NonLocalReturnInfo {
@@ -63,7 +63,7 @@ public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
if (result == 0) {
result = instructionIndex(t1.startLabel) - instructionIndex(t2.startLabel)
if (result == 0) {
assert(false, "Error: support multicatch finallies!")
assert(false, "Error: support multicatch finallies: ${t1.handler}, ${t2.handler}")
result = instructionIndex(t1.endLabel) - instructionIndex(t2.endLabel)
}
}
@@ -123,13 +123,15 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
fun processCurrent(curIns: LabelNode, directOrder: Boolean) {
getInterval(curIns, directOrder).forEach {
val b = currentIntervals.add(it)
assert(b, "Wrong interval structure: $curIns, $it")
val added = currentIntervals.add(it)
if (!added) {
assert(added, "Wrong interval structure: $curIns, $it")
}
}
getInterval(curIns, !directOrder).forEach {
val b = currentIntervals.remove(it)
assert(b, "Wrong interval structure: $curIns, $it")
val removed = currentIntervals.remove(it)
assert(removed, "Wrong interval structure: $curIns, $it")
}
}
@@ -144,6 +146,19 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
return splittedPair
}
fun splitAndRemoveInterval(interval: T, by : Interval, keepStart: Boolean): SplittedPair<T> {
val splittedPair = interval.split(by, keepStart)
if (!keepStart) {
remapStartLabel(splittedPair.newPart.startLabel, splittedPair.patchedPart)
} else {
remapEndLabel(splittedPair.newPart.endLabel, splittedPair.patchedPart)
}
val removed = currentIntervals.remove(splittedPair.patchedPart)
assert(removed, "Wrong interval structure: $splittedPair")
addNewInterval(splittedPair.newPart)
return splittedPair
}
fun getInterval(curIns: LabelNode, isOpen: Boolean) = if (isOpen) intervalStarts.get(curIns) else intervalEnds.get(curIns)
}
@@ -661,14 +661,18 @@ public class InlineCodegen extends CallGenerator {
DefaultProcessor processor = new DefaultProcessor(intoNode, offsetForFinallyLocalVar);
int curFinallyDeep = 0;
AbstractInsnNode curInstr = intoNode.instructions.getFirst();
while (curInstr != null) {
processor.processInstruction(curInstr, true);
if (InlineCodegenUtil.isFinallyStart(curInstr)) {
//TODO deep index calc could be more precise
curFinallyDeep = InlineCodegenUtil.getConstant(curInstr.getPrevious());
}
MethodInliner.PointForExternalFinallyBlocks extension = extensionPoints.get(curInstr);
if (extension != null) {
Label start = new Label();
//Label end = new Label();
MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode();
finallyNode.visitLabel(start);
@@ -676,7 +680,7 @@ public class InlineCodegen extends CallGenerator {
ExpressionCodegen finallyCodegen =
new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(),
codegen.getContext(), codegen.getState(), codegen.getParentCodegen());
finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements());
finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements(), curFinallyDeep);
FrameMap frameMap = finallyCodegen.getFrameMap();
FrameMap.Mark mark = frameMap.mark();
@@ -685,14 +689,14 @@ public class InlineCodegen extends CallGenerator {
}
finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType, extension.labelNode.getLabel());
//finallyNode.visitLabel(end);
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, curInstr);
SimpleInterval splitBy = new SimpleInterval((LabelNode) start.info, extension.labelNode);
processor.getTryBlocksMetaInfo().splitCurrentIntervals(splitBy, false);
processor.getTryBlocksMetaInfo().splitCurrentIntervals(splitBy, true);
processor.getLocalVarsMetaInfo().splitCurrentIntervals(splitBy, false);
processor.getLocalVarsMetaInfo().splitCurrentIntervals(splitBy, true);
mark.dropTo();
}
@@ -33,9 +33,9 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.load.java.JvmAbi;
import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinder;
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils;
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinder;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
@@ -77,7 +77,8 @@ public class InlineCodegenUtil {
public static final String INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker";
public static final String INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall";
public static final String INLINE_MARKER_AFTER_METHOD_NAME = "afterInlineCall";
public static final String INLINE_MARKER_GOTO_TRY_CATCH_BLOCK_END = "goToTryCatchBlockEnd";
public static final String INLINE_MARKER_FINALLY_START = "finallyStart";
public static final String INLINE_MARKER_FINALLY_END = "finallyEnd";
@Nullable
public static SMAPAndMethodNode getMethodNode(
@@ -321,7 +322,9 @@ public class InlineCodegenUtil {
//marked return could be either non-local or local in case of labeled lambda self-returns
public static boolean isMarkedReturn(@NotNull AbstractInsnNode returnIns) {
assert isReturnOpcode(returnIns.getOpcode()) : "Should be called on return instruction, but " + returnIns;
if (!isReturnOpcode(returnIns.getOpcode())) {
return false;
}
AbstractInsnNode globalFlag = returnIns.getPrevious();
return globalFlag instanceof MethodInsnNode && NON_LOCAL_RETURN.equals(((MethodInsnNode)globalFlag).owner);
}
@@ -355,10 +358,6 @@ public class InlineCodegenUtil {
return new MethodNode(API, 0, "fake", "()V", null, null);
}
static boolean isLineNumberOrLabel(@Nullable AbstractInsnNode node) {
return node instanceof LineNumberNode || node instanceof LabelNode;
}
@NotNull
public static LabelNode firstLabelInChain(@NotNull LabelNode node) {
LabelNode curNode = node;
@@ -405,16 +404,39 @@ public class InlineCodegenUtil {
}
}
public static void generateGoToTryCatchBlockEndMarker(@NotNull InstructionAdapter v) {
v.invokestatic(INLINE_MARKER_CLASS_NAME, INLINE_MARKER_GOTO_TRY_CATCH_BLOCK_END, "()V", false);
public static void generateFinallyMarker(@NotNull InstructionAdapter v, int deep, boolean start) {
v.iconst(deep);
v.invokestatic(INLINE_MARKER_CLASS_NAME, start ? INLINE_MARKER_FINALLY_START : INLINE_MARKER_FINALLY_END, "(I)V", false);
}
public static boolean isGoToTryCatchBlockEnd(@NotNull AbstractInsnNode node) {
if (!(node.getPrevious() instanceof MethodInsnNode)) return false;
MethodInsnNode previous = (MethodInsnNode) node.getPrevious();
return node.getOpcode() == Opcodes.GOTO &&
INLINE_MARKER_CLASS_NAME.equals(previous.owner) &&
INLINE_MARKER_GOTO_TRY_CATCH_BLOCK_END.equals(previous.name);
public static boolean isFinallyEnd(@NotNull AbstractInsnNode node) {
return isFinallyMarker(node, INLINE_MARKER_FINALLY_END);
}
public static boolean isFinallyStart(@NotNull AbstractInsnNode node) {
return isFinallyMarker(node, INLINE_MARKER_FINALLY_START);
}
public static boolean isFinallyMarker(@Nullable AbstractInsnNode node) {
return isFinallyMarker(node, INLINE_MARKER_FINALLY_END) || isFinallyMarker(node, INLINE_MARKER_FINALLY_START);
}
public static boolean isFinallyMarker(@Nullable AbstractInsnNode node, String name) {
if (!(node instanceof MethodInsnNode)) return false;
MethodInsnNode method = (MethodInsnNode) node;
return INLINE_MARKER_CLASS_NAME.equals(method.owner) && name.equals(method.name);
}
public static int getConstant(AbstractInsnNode ins) {
int opcode = ins.getOpcode();
Integer value;
if (opcode >= Opcodes.ICONST_0 && opcode <= Opcodes.ICONST_5) {
value = opcode - Opcodes.ICONST_0;
} else {
LdcInsnNode index = (LdcInsnNode) ins;
value = (Integer) index.cst;
}
return value;
}
public static class LabelTextifier extends Textifier {
@@ -22,7 +22,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.kotlin.codegen.AsmUtil;
import org.jetbrains.kotlin.codegen.optimization.common.CommonPackage;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.Type;
@@ -44,7 +44,10 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
final AbstractInsnNode endInsExclusive;
private FinallyBlockInfo(@NotNull AbstractInsnNode inclusiveStart, @NotNull AbstractInsnNode exclusiveEnd) {
private FinallyBlockInfo(
@NotNull AbstractInsnNode inclusiveStart,
@NotNull AbstractInsnNode exclusiveEnd
) {
startIns = inclusiveStart;
endInsExclusive = exclusiveEnd;
}
@@ -139,8 +142,9 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
}
AbstractInsnNode markedReturn = curIns;
final AbstractInsnNode instrInsertFinallyBefore = markedReturn.getPrevious();
AbstractInsnNode nextPrev = instrInsertFinallyBefore.getPrevious();
AbstractInsnNode instrInsertFinallyBefore = markedReturn.getPrevious();
AbstractInsnNode nextPrev = instrInsertFinallyBefore.getPrevious().getPrevious();
LabelNode newFinallyEnd = (LabelNode) markedReturn.getNext();
Type nonLocalReturnType = InlineCodegenUtil.getReturnType(markedReturn.getOpcode());
//Generally there could be several tryCatch blocks (group) on one code interval (same start and end labels, but maybe different handlers) -
@@ -148,25 +152,33 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
// Each group that corresponds to try/*catches*/finally contains tryCatch block with default handler.
// For each such group we should insert corresponding finally before non-local return.
// So we split all try blocks on current instructions to groups and process them independently
List<TryBlockCluster<TryCatchBlockNodeInfo>> clustersFromInnermost = InlinePackage.doClustering(currentCoveringNodesFromInnermost);
List<TryBlockCluster<TryCatchBlockNodeInfo>> clustersFromInnermost = InlinePackage.doClustering(
currentCoveringNodesFromInnermost);
Iterator<TryBlockCluster<TryCatchBlockNodeInfo>> tryCatchBlockIterator = clustersFromInnermost.iterator();
checkClusterInvariant(clustersFromInnermost);
List<TryCatchBlockNodeInfo> additionalNodesToSplit = new ArrayList<TryCatchBlockNodeInfo>();
int originalDeepIndex = 0;
while (tryCatchBlockIterator.hasNext()) {
TryBlockCluster<TryCatchBlockNodeInfo> clusterToFindFinally = tryCatchBlockIterator.next();
List<TryCatchBlockNodeInfo> clusterBlocks = clusterToFindFinally.getBlocks();
TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1);
FinallyBlockInfo finallyInfo = findFinallyBlockBody(nodeWithDefaultHandlerIfExists, getTryBlocksMetaInfo().getAllIntervals());
if (finallyInfo == null) continue;
if (nodeWithDefaultHandlerIfExists.getOnlyCopyNotProcess()) {
additionalNodesToSplit.addAll(clusterBlocks);
continue;
//lambdas finally generated before non-local return instruction,
//so it's a gap in try/catch handlers
throw new RuntimeException("Lambda try blocks should be skipped");
}
FinallyBlockInfo finallyInfo = findFinallyBlockBody(nodeWithDefaultHandlerIfExists, getTryBlocksMetaInfo().getAllIntervals());
if (finallyInfo == null) continue;
TryCatchBlockNodeInfo defaultHandlerBlock = clusterToFindFinally.getDefaultHandler();
assert defaultHandlerBlock != null;
originalDeepIndex++;
instructions.resetLabels();
List<TryCatchBlockNodePosition> tryCatchBlockInlinedInFinally = findTryCatchBlocksInlinedInFinally(finallyInfo);
@@ -174,7 +186,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
//Creating temp node for finally block copy with some additional instruction
MethodNode finallyBlockCopy = createEmptyMethodNode();
Label newFinallyStart = new Label();
Label newFinallyEnd = new Label();
Label insertedBlockEnd = new Label();
boolean generateAloadAstore = nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty();
@@ -193,13 +204,11 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
!(currentIns instanceof JumpInsnNode) ||
labelsInsideFinally.contains(((JumpInsnNode) currentIns).label);
copyInstruction(nextTempNonLocalVarIndex, markedReturn, instrInsertFinallyBefore, nonLocalReturnType, finallyBlockCopy,
currentIns, isInsOrJumpInsideFinally);
copyInstruction(finallyBlockCopy, currentIns, isInsOrJumpInsideFinally, originalDeepIndex);
currentIns = currentIns.getNext();
}
finallyBlockCopy.visitLabel(newFinallyEnd);
if (generateAloadAstore) {
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nextTempNonLocalVarIndex);
nextTempNonLocalVarIndex += nonLocalReturnType.getSize(); //TODO: do more wise indexing
@@ -211,7 +220,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore);
updateExceptionTable(clusterBlocks, newFinallyStart, newFinallyEnd,
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info, additionalNodesToSplit);
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info);
}
//skip just inserted finally
@@ -237,38 +246,22 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
}
private static void copyInstruction(
int nextTempNonLocalVarIndex,
@NotNull AbstractInsnNode curIns,
@NotNull AbstractInsnNode instrInsertFinallyBefore,
@NotNull Type nonLocalReturnType,
@NotNull MethodNode finallyBlockCopy,
@NotNull AbstractInsnNode currentIns,
boolean isInsOrJumpInsideFinally
boolean isInsOrJumpInsideFinally,
int deep
) {
//This condition allows another model for non-local returns processing
if (false) {
boolean isReturnForSubstitution =
InlineCodegenUtil.isReturnOpcode(currentIns.getOpcode()) && !InlineCodegenUtil.isMarkedReturn(currentIns);
if (!isInsOrJumpInsideFinally || isReturnForSubstitution) {
//substitute all local returns and jumps outside finally with non-local return
Type localReturnType = InlineCodegenUtil.getReturnType(currentIns.getOpcode());
substituteReturnValueInFinally(nextTempNonLocalVarIndex, nonLocalReturnType, finallyBlockCopy,
localReturnType, isReturnForSubstitution);
instrInsertFinallyBefore.accept(finallyBlockCopy);
curIns.accept(finallyBlockCopy);
if (isInsOrJumpInsideFinally) {
if (InlineCodegenUtil.isFinallyMarker(currentIns.getNext())) {
Integer constant = getConstant(currentIns);
finallyBlockCopy.visitLdcInsn(constant + deep);
} else {
currentIns.accept(finallyBlockCopy); //VISIT
}
}
else {
if (isInsOrJumpInsideFinally) {
currentIns.accept(finallyBlockCopy); //VISIT
}
else {
//keep original jump: add currentIns clone
finallyBlockCopy.instructions.add(new JumpInsnNode(currentIns.getOpcode(), ((JumpInsnNode) currentIns).label));
}
//keep original jump: add currentIns clone
finallyBlockCopy.instructions.add(new JumpInsnNode(currentIns.getOpcode(), ((JumpInsnNode) currentIns).label));
}
}
@@ -306,11 +299,10 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
private void updateExceptionTable(
@NotNull List<TryCatchBlockNodeInfo> updatingClusterBlocks,
@NotNull Label newFinallyStart,
@NotNull Label newFinallyEnd,
@NotNull LabelNode newFinallyEnd,
@NotNull List<TryCatchBlockNodePosition> tryCatchBlockPresentInFinally,
@NotNull Set<LabelNode> labelsInsideFinally,
@NotNull LabelNode insertedBlockEnd,
@NotNull List<TryCatchBlockNodeInfo> patched
@NotNull LabelNode insertedBlockEnd
) {
//copy tryCatchFinallies that totally in finally block
@@ -379,7 +371,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
getTryBlocksMetaInfo()
.split(endNode, new SimpleInterval((LabelNode) endNode.getNode().end.getLabel().info,
(LabelNode) insertedBlockEnd.getLabel().info), false);
//nextPrev = (AbstractInsnNode) insertedBlockEnd.getLabel().info;
}
handler2Cluster.clear();
@@ -387,19 +378,16 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
}
assert handler2Cluster.isEmpty() : "Unmatched clusters " + handler2Cluster.size();
List<TryCatchBlockNodeInfo > toProcess = new ArrayList<TryCatchBlockNodeInfo>();
toProcess.addAll(patched);
toProcess.addAll(updatingClusterBlocks);
patched.clear();
SimpleInterval splitBy = new SimpleInterval((LabelNode) newFinallyStart.info, (LabelNode) newFinallyEnd.info);
SimpleInterval splitBy = new SimpleInterval((LabelNode) newFinallyStart.info, newFinallyEnd);
// Inserted finally shouldn't be handled by corresponding catches,
// so we should split original interval by inserted finally one
for (TryCatchBlockNodeInfo block : toProcess) {
for (TryCatchBlockNodeInfo block : updatingClusterBlocks) {
//update exception mapping
tryBlocksMetaInfo.split(block, splitBy, false);
SplittedPair<TryCatchBlockNodeInfo> split = tryBlocksMetaInfo.splitAndRemoveInterval(block, splitBy, false);
checkFinally(split.getNewPart());
checkFinally(split.getPatchedPart());
//block patched in split method
assert !block.isEmpty() : "Finally block should be non-empty";
patched.add(block);
//TODO add assert
}
@@ -432,15 +420,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
@NotNull TryCatchBlockNodeInfo tryCatchBlock,
@ReadOnly @NotNull List<TryCatchBlockNodeInfo> tryCatchBlocks
) {
if (tryCatchBlock.getOnlyCopyNotProcess()) {
AbstractInsnNode start = new LabelNode();
AbstractInsnNode end = new LabelNode();
InsnList insnList = new InsnList();
insnList.add(start);
insnList.add(end);
return new FinallyBlockInfo(start, end);
}
List<TryCatchBlockNodeInfo> sameDefaultHandler = new ArrayList<TryCatchBlockNodeInfo>();
LabelNode defaultHandler = null;
boolean afterStartBlock = false;
@@ -469,29 +448,42 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
TryCatchBlockNodeInfo nextIntervalWithSameDefaultHandler = sameDefaultHandler.get(1);
AbstractInsnNode startFinallyChain = tryCatchBlock.getNode().end;
AbstractInsnNode endFinallyChainExclusive = skipLastGotoIfNeeded(nextIntervalWithSameDefaultHandler.getNode().start);
AbstractInsnNode meaningful = getNextMeaningful(startFinallyChain);
assert meaningful != null : "Can't find meaningful in finally block" + startFinallyChain;
Integer finallyDeep = getConstant(meaningful);
AbstractInsnNode endFinallyChainExclusive = nextIntervalWithSameDefaultHandler.getNode().start;
AbstractInsnNode current = meaningful.getNext();
while (endFinallyChainExclusive != current) {
current = current.getNext();
if (InlineCodegenUtil.isFinallyEnd(current)) {
Integer currentDeep = getConstant(current.getPrevious());
if (currentDeep.equals(finallyDeep)) {
endFinallyChainExclusive = current.getNext();
break;
}
}
}
FinallyBlockInfo finallyInfo = new FinallyBlockInfo(startFinallyChain.getNext(), endFinallyChainExclusive);
if (inlineFun.instructions.indexOf(finallyInfo.startIns) > inlineFun.instructions.indexOf(finallyInfo.endInsExclusive)) {
throw new AssertionError("Inconsistent finally: block end occurs before start " + traceInterval(finallyInfo.endInsExclusive, finallyInfo.startIns));
}
checkFinally(finallyInfo);
return finallyInfo;
}
@NotNull
private static AbstractInsnNode skipLastGotoIfNeeded(
@NotNull AbstractInsnNode lastFinallyInsExclusive
) {
private void checkFinally(FinallyBlockInfo finallyInfo) {
checkFinally(finallyInfo.startIns, finallyInfo.endInsExclusive);
}
AbstractInsnNode prevLast = getPrevNoLineNumberOrLabel(lastFinallyInsExclusive, true);
assert prevLast != null : "Empty finally block: " + lastFinallyInsExclusive;
private void checkFinally(IntervalWithHandler intervalWithHandler) {
checkFinally(intervalWithHandler.getStartLabel(), intervalWithHandler.getEndLabel());
}
if (InlineCodegenUtil.isGoToTryCatchBlockEnd(prevLast)) {
return prevLast.getPrevious();
private void checkFinally(AbstractInsnNode startIns, AbstractInsnNode endInsExclusive) {
if (inlineFun.instructions.indexOf(startIns) >= inlineFun.instructions.indexOf(endInsExclusive)) {
throw new AssertionError("Inconsistent finally: block end occurs before start " + traceInterval(endInsExclusive, startIns));
}
return lastFinallyInsExclusive;
}
@NotNull
@@ -529,26 +521,11 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
return result;
}
private static void substituteReturnValueInFinally(
int nonLocalVarIndex,
@NotNull Type nonLocalReturnType,
@NotNull MethodNode finallyBlockCopy,
@NotNull Type localReturnType,
boolean doPop
) {
if (doPop && localReturnType != Type.VOID_TYPE) {
AsmUtil.pop(finallyBlockCopy, localReturnType);
}
if (nonLocalReturnType != Type.VOID_TYPE) {
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nonLocalVarIndex);
}
}
@Nullable
private static AbstractInsnNode getPrevNoLineNumberOrLabel(@NotNull AbstractInsnNode node, boolean strict) {
AbstractInsnNode result = strict ? node.getPrevious() : node;
while (isLineNumberOrLabel(result)) {
result = result.getPrevious();
private static AbstractInsnNode getNextMeaningful(@NotNull AbstractInsnNode node) {
AbstractInsnNode result = node.getNext();
while (result != null && !CommonPackage.getIsMeaningful(result)) {
result = result.getNext();
}
return result;
}
@@ -21,9 +21,11 @@ import org.jetbrains.annotations.Nullable;
class InvokeCall {
private final int index;
public final LambdaInfo lambdaInfo;
public final int finallyDeep;
InvokeCall(int index, @Nullable LambdaInfo lambdaInfo) {
InvokeCall(int index, @Nullable LambdaInfo lambdaInfo, int finallyDeep) {
this.index = index;
this.lambdaInfo = lambdaInfo;
this.finallyDeep = finallyDeep;
}
}
@@ -104,9 +104,19 @@ public class MethodInliner {
@NotNull LocalVarRemapper remapper,
boolean remapReturn,
@NotNull LabelOwner labelOwner
) {
return doInline(adapter, remapper, remapReturn, labelOwner, 0);
}
private InlineResult doInline(
@NotNull MethodVisitor adapter,
@NotNull LocalVarRemapper remapper,
boolean remapReturn,
@NotNull LabelOwner labelOwner,
int finallyDeepShift
) {
//analyze body
MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node);
MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node, finallyDeepShift);
//substitute returns with "goto end" instruction to keep non local returns in lambdas
Label end = new Label();
@@ -234,7 +244,7 @@ public class MethodInliner {
mapper);
LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift);
InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info);//TODO add skipped this and receiver
InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info, invokeCall.finallyDeep);//TODO add skipped this and receiver
result.addAllClassesToRemove(lambdaResult);
//return value boxing/unboxing
@@ -301,7 +311,7 @@ public class MethodInliner {
}
@NotNull
public MethodNode prepareNode(@NotNull MethodNode node) {
public MethodNode prepareNode(@NotNull MethodNode node, int finallyDeepShift) {
final int capturedParamsSize = parameters.getCaptured().size();
final int realParametersSize = parameters.getReal().size();
Type[] types = Type.getArgumentTypes(node.desc);
@@ -354,13 +364,14 @@ public class MethodInliner {
node.accept(transformedNode);
transformCaptured(transformedNode);
transformFinallyDeepIndex(transformedNode, finallyDeepShift);
return transformedNode;
}
@NotNull
protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) {
node = prepareNode(node);
protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node, int finallyDeepShift) {
node = prepareNode(node, finallyDeepShift);
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()) {
@NotNull
@@ -395,6 +406,7 @@ public class MethodInliner {
int index = 0;
boolean awaitClassReification = false;
int currentFinallyDeep = 0;
while (cur != null) {
Frame<SourceValue> frame = sources[index];
@@ -404,6 +416,11 @@ public class MethodInliner {
awaitClassReification = true;
}
else if (cur.getType() == AbstractInsnNode.METHOD_INSN) {
if (InlineCodegenUtil.isFinallyStart(cur)) {
//TODO deep index calc could be more precise
currentFinallyDeep = InlineCodegenUtil.getConstant(cur.getPrevious());
}
MethodInsnNode methodInsnNode = (MethodInsnNode) cur;
String owner = methodInsnNode.owner;
String desc = methodInsnNode.desc;
@@ -426,7 +443,7 @@ public class MethodInliner {
}
}
invokeCalls.add(new InvokeCall(varIndex, lambdaInfo));
invokeCalls.add(new InvokeCall(varIndex, lambdaInfo, currentFinallyDeep));
}
else if (isAnonymousConstructorCall(owner, name)) {
Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>();
@@ -585,6 +602,23 @@ public class MethodInliner {
}
}
private void transformFinallyDeepIndex(@NotNull MethodNode node, int finallyDeepShift) {
if (finallyDeepShift == 0) {
return;
}
AbstractInsnNode cur = node.instructions.getFirst();
while (cur != null) {
if (cur instanceof MethodInsnNode && InlineCodegenUtil.isFinallyMarker(cur)) {
AbstractInsnNode constant = cur.getPrevious();
int curDeep = InlineCodegenUtil.getConstant(constant);
node.instructions.insert(constant, new LdcInsnNode(curDeep + finallyDeepShift));
node.instructions.remove(constant);
}
cur = cur.getNext();
}
}
@NotNull
public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) {
List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>();
@@ -701,7 +735,8 @@ public class MethodInliner {
//genetate finally block before nonLocalReturn flag/return/goto
LabelNode label = new LabelNode();
instructions.insert(insnNode, label);
result.add(new PointForExternalFinallyBlocks(isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode()),
result.add(new PointForExternalFinallyBlocks(getInstructionToInsertFinallyBefore(insnNode, isLocalReturn),
getReturnType(insnNode.getOpcode()),
label));
}
insnNode = insnNode.getNext();
@@ -709,6 +744,11 @@ public class MethodInliner {
return result;
}
@NotNull
private static AbstractInsnNode getInstructionToInsertFinallyBefore(@NotNull AbstractInsnNode nonLocalReturnOrJump, boolean isLocal) {
return isLocal ? nonLocalReturnOrJump : nonLocalReturnOrJump.getPrevious();
}
//Place to insert finally blocks from try blocks that wraps inline fun call
public static class PointForExternalFinallyBlocks {
@@ -0,0 +1,94 @@
import test.*
import Kind.*
enum class Kind {
LOCAL
EXTERNAL
GLOBAL
}
val FINALLY_CHAIN = "in local finally, in doCall finally, in external finally, in doCall finally, in global finally"
class Internal(val value: String)
class External(val value: String)
class Global(val value: String)
fun test1(intKind: Kind, extKind: Kind, holder: Holder): Global {
holder.value = ""
try {
var externalResult = doCall (ext@ {
try {
val internalResult = doCall (int@ {
try {
if (intKind == Kind.GLOBAL) {
return@test1 Global("internal -> global")
}
else if (intKind == EXTERNAL) {
return@ext External("internal -> external")
}
return@int Internal("internal -> local")
}
finally {
holder.value += "in local finally"
}
}, holder)
if (extKind == GLOBAL || extKind == EXTERNAL) {
return Global("external -> global")
}
External(internalResult.value + ": external -> local");
}
finally {
holder.value += ", in external finally"
}
}, holder)
return Global(externalResult.value + ": exit")
}
finally {
holder.value += ", in global finally"
}
}
fun box(): String {
var holder = Holder()
var test1 = test1(LOCAL, LOCAL, holder).value
if (holder.value != FINALLY_CHAIN || test1 != "internal -> local: external -> local: exit") return "test1: ${test1}, finally = ${holder.value}"
test1 = test1(EXTERNAL, LOCAL, holder).value
if (holder.value != FINALLY_CHAIN || test1 != "internal -> external: exit") return "test2: ${test1}, finally = ${holder.value}"
test1 = test1(GLOBAL, LOCAL, holder).value
if (holder.value != FINALLY_CHAIN || test1 != "internal -> global") return "test3: ${test1}, finally = ${holder.value}"
test1 = test1(LOCAL, EXTERNAL, holder).value
if (holder.value != FINALLY_CHAIN || test1 != "external -> global") return "test4: ${test1}, finally = ${holder.value}"
test1 = test1(EXTERNAL, EXTERNAL, holder).value
if (holder.value != FINALLY_CHAIN || test1 != "internal -> external: exit") return "test5: ${test1}, finally = ${holder.value}"
test1 = test1(GLOBAL, EXTERNAL, holder).value
if (holder.value != FINALLY_CHAIN || test1 != "internal -> global") return "test6: ${test1}, finally = ${holder.value}"
test1 = test1(LOCAL, GLOBAL, holder).value
if (holder.value != FINALLY_CHAIN || test1 != "external -> global") return "test7: ${test1}, finally = ${holder.value}"
test1 = test1(EXTERNAL, GLOBAL, holder).value
if (holder.value != FINALLY_CHAIN || test1 != "internal -> external: exit") return "test8: ${test1}, finally = ${holder.value}"
test1 = test1(GLOBAL, GLOBAL, holder).value
if (holder.value != FINALLY_CHAIN || test1 != "internal -> global") return "test9: ${test1}, finally = ${holder.value}"
return "OK"
}
@@ -0,0 +1,13 @@
package test
class Holder {
var value: String = ""
}
inline fun <R> doCall(block: ()-> R, h: Holder) : R {
try {
return block()
} finally {
h.value += ", in doCall finally"
}
}
@@ -0,0 +1,69 @@
import test.*
class Holder {
var value: String = ""
}
fun test1(h: Holder, doReturn: Int): String {
doCall (
{
if (doReturn < 1) {
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
}
h.value += "LOCAL"
"OK_LOCAL"
},
{
h.value += ", OF_FINALLY1"
return "OF_FINALLY1"
}
)
return "LOCAL";
}
fun test2(h: Holder, doReturn: Int): String {
doCall (
{
if (doReturn < 1) {
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
}
h.value += "LOCAL"
"OK_LOCAL"
},
{
try {
h.value += ", OF_FINALLY1"
return "OF_FINALLY1"
} finally {
h.value += ", OF_FINALLY1_FINALLY"
}
}
)
return "FAIL";
}
fun box(): String {
var h = Holder()
val test10 = test1(h, 0)
if (test10 != "OF_FINALLY1" || h.value != "OK_NONLOCAL, OF_FINALLY1") return "test10: ${test10}, holder: ${h.value}"
h = Holder()
val test11 = test1(h, 1)
if (test11 != "OF_FINALLY1" || h.value != "LOCAL, OF_FINALLY1") return "test11: ${test11}, holder: ${h.value}"
h = Holder()
val test2 = test2(h, 0)
if (test2 != "OF_FINALLY1" || h.value != "OK_NONLOCAL, OF_FINALLY1, OF_FINALLY1_FINALLY") return "test20: ${test2}, holder: ${h.value}"
h = Holder()
val test21 = test2(h, 1)
if (test21 != "OF_FINALLY1" || h.value != "LOCAL, OF_FINALLY1, OF_FINALLY1_FINALLY") return "test21: ${test21}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,9 @@
package test
public inline fun doCall(block: ()-> Unit, finallyBlock1: ()-> Unit) {
try {
block()
} finally {
finallyBlock1()
}
}
@@ -0,0 +1,53 @@
import test.*
fun test1(h: Holder, doReturn: Int): String {
doCall_1 (
{
if (doReturn < 1) {
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
}
h.value += "LOCAL"
"OK_LOCAL"
},
h
)
return "TEST1";
}
fun test2(h: Holder, doReturn: Int): String {
doCall_2 (
{
if (doReturn < 1) {
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
}
h.value += "LOCAL"
"OK_LOCAL"
},
h
)
return "TEST2";
}
fun box(): String {
var h = Holder()
val test10 = test1(h, 0)
if (test10 != "TEST1" || h.value != "OK_NONLOCAL, OF_FINALLY1, DO_CALL_1_FINALLY") return "test10: ${test10}, holder: ${h.value}"
h = Holder()
val test11 = test1(h, 1)
if (test11 != "TEST1" || h.value != "LOCAL, OF_FINALLY1, DO_CALL_1_FINALLY") return "test11: ${test11}, holder: ${h.value}"
h = Holder()
val test2 = test2(h, 0)
if (test2 != "TEST2" || h.value != "OK_NONLOCAL, OF_FINALLY1, OF_FINALLY1_FINALLY, DO_CALL_1_FINALLY") return "test20: ${test2}, holder: ${h.value}"
h = Holder()
val test21 = test2(h, 1)
if (test21 != "TEST2" || h.value != "LOCAL, OF_FINALLY1, OF_FINALLY1_FINALLY, DO_CALL_1_FINALLY") return "test21: ${test21}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,40 @@
package test
class Holder {
var value: String = ""
}
inline fun doCall_1(block: ()-> Unit, h: Holder) {
try {
doCall(block) {
h.value += ", OF_FINALLY1"
return
}
} finally {
h.value += ", DO_CALL_1_FINALLY"
}
}
inline fun doCall_2(block: ()-> Unit, h: Holder) {
try {
doCall(block) {
try {
h.value += ", OF_FINALLY1"
return
}
finally {
h.value += ", OF_FINALLY1_FINALLY"
}
}
} finally {
h.value += ", DO_CALL_1_FINALLY"
}
}
inline fun doCall(block: ()-> Unit, finallyBlock1: ()-> Unit) {
try {
block()
} finally {
finallyBlock1()
}
}
@@ -0,0 +1,53 @@
import test.*
fun test1(h: Holder, doReturn: Int): String {
doCall_1 (
{
if (doReturn < 1) {
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
}
h.value += "LOCAL"
"OK_LOCAL"
},
h
)
return "TEST1";
}
fun test2(h: Holder, doReturn: Int): String {
doCall_2 (
{
if (doReturn < 1) {
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
}
h.value += "LOCAL"
"OK_LOCAL"
},
h
)
return "TEST2";
}
fun box(): String {
var h = Holder()
val test10 = test1(h, 0)
if (test10 != "OK_NONLOCAL" || h.value != "OK_NONLOCAL, OF_FINALLY1, DO_CALL_1_FINALLY") return "test10: ${test10}, holder: ${h.value}"
h = Holder()
val test11 = test1(h, 1)
if (test11 != "TEST1" || h.value != "LOCAL, OF_FINALLY1, DO_CALL_1_FINALLY") return "test11: ${test11}, holder: ${h.value}"
h = Holder()
val test2 = test2(h, 0)
if (test2 != "OK_NONLOCAL" || h.value != "OK_NONLOCAL, OF_FINALLY1, OF_FINALLY1_FINALLY, DO_CALL_1_FINALLY") return "test20: ${test2}, holder: ${h.value}"
h = Holder()
val test21 = test2(h, 1)
if (test21 != "TEST2" || h.value != "LOCAL, OF_FINALLY1, OF_FINALLY1_FINALLY, DO_CALL_1_FINALLY") return "test21: ${test21}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,38 @@
package test
class Holder {
var value: String = ""
}
inline fun doCall_1(block: ()-> Unit, h: Holder) {
try {
doCall(block) {
h.value += ", OF_FINALLY1"
}
} finally {
h.value += ", DO_CALL_1_FINALLY"
}
}
inline fun doCall_2(block: ()-> Unit, h: Holder) {
try {
doCall(block) {
try {
h.value += ", OF_FINALLY1"
}
finally {
h.value += ", OF_FINALLY1_FINALLY"
}
}
} finally {
h.value += ", DO_CALL_1_FINALLY"
}
}
inline fun doCall(block: ()-> Unit, finallyBlock1: ()-> Unit) {
try {
block()
} finally {
finallyBlock1()
}
}
@@ -0,0 +1,30 @@
import test.*
fun test0(h: Holder, throwException: Boolean): Int {
val localResult = doCall2_2 (
{
h.value += "OK_NONLOCAL"
if (throwException) {
throw java.lang.RuntimeException()
}
return 1
},
"FAIL",
h
)
return -1;
}
fun box(): String {
var h = Holder()
val test0 = test0(h, true)
if (test0 != -1 || h.value != "OK_NONLOCAL, OK_EXCEPTION, OK_FINALLY") return "test0: ${test0}, holder: ${h.value}"
h = Holder()
val test1 = test0(h, false)
if (test1 != 1 || h.value != "OK_NONLOCAL, OK_FINALLY") return "test1: ${test1}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,32 @@
package test
public class Holder {
public var value: String = ""
}
public inline fun <R> doCall2_2(block: () -> R, res: R, h: Holder): R {
return doCall2_1(block, {
h.value += ", OK_EXCEPTION"
"OK_EXCEPTION"
}, res, h)
}
public inline fun <R> doCall2_1(block: () -> R, exception: (e: Exception) -> Unit, res: R, h: Holder): R {
return doCall2<R>(block, exception, {
h.value += ", OK_FINALLY"
"OK_FINALLY"
}, res)
}
public inline fun <R> doCall2(block: () -> R, exception: (e: Exception) -> Unit, finallyBlock: () -> Unit, res: R): R {
try {
return block()
}
catch (e: Exception) {
exception(e)
}
finally {
finallyBlock()
}
return res
}
@@ -0,0 +1,30 @@
import test.*
fun test0(h: Holder, throwException: Boolean): Int {
val localResult = doCall2_2 (
{
h.value += "OK_NONLOCAL"
if (throwException) {
throw java.lang.RuntimeException()
}
return 1
},
"FAIL",
h
)
return -1;
}
fun box(): String {
var h = Holder()
val test0 = test0(h, true)
if (test0 != -1 || h.value != "OK_NONLOCAL, OK_EXCEPTION, OK_FINALLY, DO_CALL_2_FINALLY") return "test0: ${test0}, holder: ${h.value}"
h = Holder()
val test1 = test0(h, false)
if (test1 != 1 || h.value != "OK_NONLOCAL, OK_FINALLY, DO_CALL_2_FINALLY") return "test1: ${test1}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,36 @@
package test
public class Holder {
public var value: String = ""
}
public inline fun <R> doCall2_2(block: () -> R, res: R, h: Holder): R {
return doCall2_1(block, {
h.value += ", OK_EXCEPTION"
"OK_EXCEPTION"
}, res, h)
}
public inline fun <R> doCall2_1(block: () -> R, exception: (e: Exception) -> Unit, res: R, h: Holder): R {
return doCall2<R>(block, exception, {
h.value += ", OK_FINALLY"
"OK_FINALLY"
}, res, h)
}
public inline fun <R> doCall2(block: () -> R, exception: (e: Exception) -> Unit, finallyBlock: () -> Unit, res: R, h: Holder): R {
try {
try {
return block()
}
catch (e: Exception) {
exception(e)
}
finally {
finallyBlock()
}
} finally {
h.value += ", DO_CALL_2_FINALLY"
}
return res
}
@@ -0,0 +1,30 @@
import test.*
fun test0(h: Holder, throwException: Boolean): Int {
val localResult = doCall2_2 (
{
h.value += "OK_NONLOCAL"
if (throwException) {
throw java.lang.RuntimeException()
}
return 1
},
"FAIL",
h
)
return -1;
}
fun box(): String {
var h = Holder()
val test0 = test0(h, true)
if (test0 != -1 || h.value != "OK_NONLOCAL, OK_EXCEPTION, OK_FINALLY, DO_CALL_2_FINALLY, DO_CALL_2_1_FINALLY, DO_CALL_2_2_FINALLY") return "test0: ${test0}, holder: ${h.value}"
h = Holder()
val test1 = test0(h, false)
if (test1 != 1 || h.value != "OK_NONLOCAL, OK_FINALLY, DO_CALL_2_FINALLY, DO_CALL_2_1_FINALLY, DO_CALL_2_2_FINALLY") return "test1: ${test1}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,44 @@
package test
public class Holder {
public var value: String = ""
}
public inline fun <R> doCall2_2(block: () -> R, res: R, h: Holder): R {
try {
return doCall2_1(block, {
h.value += ", OK_EXCEPTION"
"OK_EXCEPTION"
}, res, h)
} finally{
h.value += ", DO_CALL_2_2_FINALLY"
}
}
public inline fun <R> doCall2_1(block: () -> R, exception: (e: Exception) -> Unit, res: R, h: Holder): R {
try {
return doCall2<R>(block, exception, {
h.value += ", OK_FINALLY"
"OK_FINALLY"
}, res, h)
} finally {
h.value += ", DO_CALL_2_1_FINALLY"
}
}
public inline fun <R> doCall2(block: () -> R, exception: (e: Exception) -> Unit, finallyBlock: () -> Unit, res: R, h: Holder): R {
try {
try {
return block()
}
catch (e: Exception) {
exception(e)
}
finally {
finallyBlock()
}
} finally {
h.value += ", DO_CALL_2_FINALLY"
}
return res
}
@@ -0,0 +1,34 @@
import test.*
fun test0(h: Holder, throwException: Boolean): Int {
val localResult = doCall2_2 (
{
h.value += "OK_NONLOCAL"
if (throwException) {
throw java.lang.RuntimeException()
}
return 1
},
"FAIL",
h
)
return -1;
}
fun box(): String {
var h = Holder()
val test0 = test0(h, true)
if (test0 != -1 || h.value != "OK_NONLOCAL, OK_EXCEPTION, OK_FINALLY, DO_CALL_2_FINALLY, DO_CALL_2_1_FINALLY, DO_CALL_2_2_FINALLY")
return "test0: ${test0}, holder: ${h.value}"
h = Holder()
val test1 = test0(h, false)
if (test1 != 1 || h.value != "OK_NONLOCAL, OK_FINALLY, DO_CALL_2_FINALLY, DO_CALL_2_1_FINALLY, DO_CALL_2_2_FINALLY")
return "test1: ${test1}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,44 @@
package test
public class Holder {
public var value: String = ""
}
public inline fun doCall2_2(block: () -> String, res: String, h: Holder): String {
try {
return doCall2_1(block, {
h.value += ", OK_EXCEPTION"
return "OK_EXCEPTION"
}, res, h)
} finally{
h.value += ", DO_CALL_2_2_FINALLY"
}
}
public inline fun <R> doCall2_1(block: () -> R, exception: (e: Exception) -> Unit, res: R, h: Holder): R {
try {
return doCall2<R>(block, exception, {
h.value += ", OK_FINALLY"
"OK_FINALLY"
}, res, h)
} finally {
h.value += ", DO_CALL_2_1_FINALLY"
}
}
public inline fun <R> doCall2(block: () -> R, exception: (e: Exception) -> Unit, finallyBlock: () -> Unit, res: R, h: Holder): R {
try {
try {
return block()
}
catch (e: Exception) {
exception(e)
}
finally {
finallyBlock()
}
} finally {
h.value += ", DO_CALL_2_FINALLY"
}
return res
}
@@ -0,0 +1,34 @@
import test.*
fun test0(h: Holder, throwException: Boolean): Int {
val localResult = doCall2_2 (
{
h.value += "OK_NONLOCAL"
if (throwException) {
throw java.lang.RuntimeException()
}
return 1
},
"FAIL",
h
)
return -1;
}
fun box(): String {
var h = Holder()
val test0 = test0(h, true)
if (test0 != -1 || h.value != "OK_NONLOCAL, OK_EXCEPTION, OK_FINALLY, OK_FINALLY_NESTED, DO_CALL_2_FINALLY, DO_CALL_2_1_FINALLY, DO_CALL_2_2_FINALLY") return "test0: ${test0}, holder: ${h.value}"
h = Holder()
val test1 = test0(h, false)
if (test1 != 1 || h.value != "OK_NONLOCAL, OK_FINALLY, OK_FINALLY_NESTED, DO_CALL_2_FINALLY, DO_CALL_2_1_FINALLY, DO_CALL_2_2_FINALLY") return "test1: ${test1}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,48 @@
package test
public class Holder {
public var value: String = ""
}
public inline fun doCall2_2(block: () -> String, res: String, h: Holder): String {
try {
return doCall2_1(block, {
h.value += ", OK_EXCEPTION"
return "OK_EXCEPTION"
}, res, h)
} finally{
h.value += ", DO_CALL_2_2_FINALLY"
}
}
public inline fun <R> doCall2_1(block: () -> R, exception: (e: Exception) -> Unit, res: R, h: Holder): R {
try {
return doCall2<R>(block, exception, {
try {
h.value += ", OK_FINALLY"
"OK_FINALLY"
} finally {
h.value += ", OK_FINALLY_NESTED"
}
}, res, h)
} finally {
h.value += ", DO_CALL_2_1_FINALLY"
}
}
public inline fun <R> doCall2(block: () -> R, exception: (e: Exception) -> Unit, finallyBlock: () -> Unit, res: R, h: Holder): R {
try {
try {
return block()
}
catch (e: Exception) {
exception(e)
}
finally {
finallyBlock()
}
} finally {
h.value += ", DO_CALL_2_FINALLY"
}
return res
}
@@ -0,0 +1,68 @@
import test.*
val FINALLY_CHAIN = "in local finally, in doCall finally, in external finally, in doCall finally, in global finally"
fun test1(holder: Holder, p: Int): String {
holder.value = "start"
return test2(holder) { i ->
if (p == i) {
return "call $i"
}
}
}
inline fun test2(holder: Holder, l: (s: Int) -> Unit): String {
try {
l(0)
var externalResult = doCall (ext@ {
l(1)
try {
l(2)
val internalResult = doCall (int@ {
l(3)
try {
l(4)
return "fail"
}
finally {
holder.value += ", in local finally"
}
}, holder)
}
finally {
holder.value += ", in external finally"
}
}, holder)
return "fail"
}
finally {
holder.value += ", in global finally"
}
}
fun box(): String {
var holder = Holder()
var test1 = test1(holder, 0)
if (holder.value != "start, in global finally" || test1 != "call 0") return "test1: ${test1}, finally = ${holder.value}"
test1 = test1(holder, 1)
if (holder.value != "start, in doCall finally, in global finally" || test1 != "call 1")
return "test2: ${test1}, finally = ${holder.value}"
test1 = test1(holder, 2)
if (holder.value != "start, in external finally, in doCall finally, in global finally" || test1 != "call 2")
return "test3: ${test1}, finally = ${holder.value}"
test1 = test1(holder, 3)
if (holder.value != "start, in doCall finally, in external finally, in doCall finally, in global finally" || test1 != "call 3")
return "test4: ${test1}, finally = ${holder.value}"
test1 = test1(holder, 4)
if (holder.value != "start, in local finally, in doCall finally, in external finally, in doCall finally, in global finally" || test1 != "call 4")
return "test5: ${test1}, finally = ${holder.value}"
return "OK"
}
@@ -0,0 +1,13 @@
package test
class Holder {
var value: String = ""
}
inline fun <R> doCall(block: ()-> R, h: Holder) : R {
try {
return block()
} finally {
h.value += ", in doCall finally"
}
}
@@ -0,0 +1,127 @@
import test.*
fun test0(h: Holder): Int {
val localResult = doCall2 (
{
h.value += "OK_NONLOCAL"
if (true) {
throw java.lang.RuntimeException()
}
return 1
},
{
h.value += ", OK_EXCEPTION"
return 2
},
{
h.value += ", OK_FINALLY"
"OK_FINALLY"
}, "FAIL", h)
return -1;
}
fun test1(h: Holder): Int {
val localResult = doCall2 (
{
h.value += "OK_NONLOCAL"
return 1
},
{
h.value += ", OK_EXCEPTION"
"OK_EXCEPTION"
},
{
h.value += ", OK_FINALLY"
"OK_FINALLY"
}, "FAIL", h)
return -1;
}
fun test2(h: Holder): String {
val localResult = doCall (
{
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
},
{
h.value += ", OK_EXCEPTION"
2
},
{
h.value += ", OK_FINALLY"
3
}, h)
return "" + localResult;
}
fun test3(h: Holder): String {
val localResult = doCall (
{
h.value += "OK_NONLOCAL"
if (true) {
throw java.lang.RuntimeException()
}
return "OK_NONLOCAL"
},
{
h.value += ", OK_EXCEPTION"
return "OK_EXCEPTION"
},
{
h.value += ", OK_FINALLY"
3
}, h)
return "" + localResult;
}
fun test4(h: Holder): String {
val localResult = doCall (
{
h.value += "OK_NONLOCAL"
if (true) {
throw java.lang.RuntimeException()
}
h.value += "fail"
return "OK_NONLOCAL"
},
{
h.value += ", OK_EXCEPTION"
return "OK_EXCEPTION"
},
{
h.value += ", OK_FINALLY"
return "OK_FINALLY"
}, h)
return "" + localResult;
}
fun box(): String {
var h = Holder()
val test0 = test0(h)
if (test0 != 2 || h.value != "OK_NONLOCAL, OK_EXCEPTION, OK_FINALLY, INLINE_CALL_FINALLY") return "test0: ${test0}, holder: ${h.value}"
h = Holder()
val test1 = test1(h)
if (test1 != 1 || h.value != "OK_NONLOCAL, OK_FINALLY, INLINE_CALL_FINALLY") return "test1: ${test1}, holder: ${h.value}"
h = Holder()
val test2 = test2(h)
if (test2 != "OK_NONLOCAL" || h.value != "OK_NONLOCAL, OK_FINALLY, INLINE_CALL_FINALLY") return "test2: ${test2}, holder: ${h.value}"
h = Holder()
val test3 = test3(h)
if (test3 != "OK_EXCEPTION" || h.value != "OK_NONLOCAL, OK_EXCEPTION, OK_FINALLY, INLINE_CALL_FINALLY") return "test3: ${test3}, holder: ${h.value}"
h = Holder()
val test4 = test4(h)
if (test4 != "OK_FINALLY" || h.value != "OK_NONLOCAL, OK_EXCEPTION, OK_FINALLY, INLINE_CALL_FINALLY") return "test4: ${test4}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,39 @@
package test
public class Holder {
public var value: String = ""
}
public inline fun doCall(block: ()-> Int, exception: (e: Exception)-> Unit, finallyBlock: ()-> Int, h : Holder, res: Int = -111) : Int {
try {
try {
return block()
}
catch (e: Exception) {
exception(e)
}
finally {
finallyBlock()
}
} finally {
h.value += ", INLINE_CALL_FINALLY"
}
return res
}
public inline fun <R> doCall2(block: ()-> R, exception: (e: Exception)-> Unit, finallyBlock: ()-> R, res: R, h : Holder) : R {
try {
try {
return block()
}
catch (e: Exception) {
exception(e)
}
finally {
finallyBlock()
}
} finally {
h.value += ", INLINE_CALL_FINALLY"
}
return res
}
@@ -0,0 +1,75 @@
import test.*
class Holder {
var value: String = ""
}
fun test1(h: Holder, doReturn: Int): String {
doCall (
{
if (doReturn < 1) {
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
}
h.value += "LOCAL"
"OK_LOCAL"
},
{
h.value += ", OK_NONLOCAL2"
return "OK_NONLOCAL2"
},
{
h.value += ", OK_FINALLY"
}
)
return "LOCAL";
}
fun test2(h: Holder, doReturn: Int): String {
doCall (
{
if (doReturn < 1) {
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
}
h.value += "LOCAL"
"OK_LOCAL"
},
{
try {
h.value += ", OK_NONLOCAL2"
return "OK_NONLOCAL2"
} finally {
h.value += ", OK_NONLOCAL2_FINALLY"
}
},
{
h.value += ", OK_FINALLY"
}
)
return "FAIL";
}
fun box(): String {
var h = Holder()
val test10 = test1(h, 0)
if (test10 != "OK_NONLOCAL" || h.value != "OK_NONLOCAL, OK_FINALLY") return "test10: ${test10}, holder: ${h.value}"
h = Holder()
val test11 = test1(h, 1)
if (test11 != "OK_NONLOCAL2" || h.value != "LOCAL, OK_NONLOCAL2, OK_FINALLY") return "test11: ${test11}, holder: ${h.value}"
h = Holder()
val test2 = test2(h, 0)
if (test2 != "OK_NONLOCAL" || h.value != "OK_NONLOCAL, OK_FINALLY") return "test20: ${test2}, holder: ${h.value}"
h = Holder()
val test21 = test2(h, 1)
if (test21 != "OK_NONLOCAL2" || h.value != "LOCAL, OK_NONLOCAL2, OK_NONLOCAL2_FINALLY, OK_FINALLY") return "test21: ${test21}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,10 @@
package test
public inline fun doCall(block: ()-> Unit, block2: ()-> Unit, finallyBlock2: ()-> Unit) {
try {
block()
block2()
} finally {
finallyBlock2()
}
}
@@ -0,0 +1,72 @@
import test.*
fun test1(h: Holder, doReturn: Int): String {
doCall (
{
if (doReturn < 1) {
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
}
h.value += "LOCAL"
"OK_LOCAL"
},
{
h.value += ", OK_NONLOCAL2"
return "OK_NONLOCAL2"
},
{
h.value += ", OK_FINALLY"
},
h
)
return "LOCAL";
}
fun test2(h: Holder, doReturn: Int): String {
doCall (
{
if (doReturn < 1) {
h.value += "OK_NONLOCAL"
return "OK_NONLOCAL"
}
h.value += "LOCAL"
"OK_LOCAL"
},
{
try {
h.value += ", OK_NONLOCAL2"
return "OK_NONLOCAL2"
} finally {
h.value += ", OK_NONLOCAL2_FINALLY"
}
},
{
h.value += ", OK_FINALLY"
},
h
)
return "FAIL";
}
fun box(): String {
var h = Holder()
val test10 = test1(h, 0)
if (test10 != "OK_NONLOCAL" || h.value != "OK_NONLOCAL, OK_FINALLY, DO_CALL_EXT_FINALLY") return "test10: ${test10}, holder: ${h.value}"
h = Holder()
val test11 = test1(h, 1)
if (test11 != "OK_NONLOCAL2" || h.value != "LOCAL, OK_NONLOCAL2, OK_FINALLY, DO_CALL_EXT_FINALLY") return "test11: ${test11}, holder: ${h.value}"
h = Holder()
val test2 = test2(h, 0)
if (test2 != "OK_NONLOCAL" || h.value != "OK_NONLOCAL, OK_FINALLY, DO_CALL_EXT_FINALLY") return "test20: ${test2}, holder: ${h.value}"
h = Holder()
val test21 = test2(h, 1)
if (test21 != "OK_NONLOCAL2" || h.value != "LOCAL, OK_NONLOCAL2, OK_NONLOCAL2_FINALLY, OK_FINALLY, DO_CALL_EXT_FINALLY") return "test21: ${test21}, holder: ${h.value}"
return "OK"
}
@@ -0,0 +1,19 @@
package test
class Holder {
var value: String = ""
}
inline fun doCall(block: ()-> Unit, block2: ()-> Unit, finallyBlock2: ()-> Unit, res: Holder) {
try {
try {
block()
block2()
}
finally {
finallyBlock2()
}
} finally {
res.value += ", DO_CALL_EXT_FINALLY"
}
}
@@ -0,0 +1,19 @@
import test.*
inline fun <T> bar(arg: String, action: () -> T) {
try {
action()
} finally {
arg.length()
}
}
fun box(): String {
foo() {
bar("") {
return "OK"
}
}
return "fail"
}
@@ -0,0 +1,13 @@
package test
inline fun baz(x: Int) {}
inline fun <T> foo(action: () -> T): T {
baz(0)
try {
return action()
} finally {
baz(1)
}
}
@@ -25,4 +25,4 @@ fun box() : String {
return "fail"
}
// 10 TRYCATCHBLOCK
// 8 TRYCATCHBLOCK
@@ -24,6 +24,5 @@ fun box() : String {
}
return "fail"
}
// maybe we should check test data
// 13 TRYCATCHBLOCK
/* 8 + 1 see notSplitedExceptionTable.kt*/
// 9 TRYCATCHBLOCK
@@ -558,6 +558,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("kt6956.1.kt")
public void testKt6956() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/kt6956.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("kt7273.1.kt")
public void testKt7273() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/kt7273.1.kt");
@@ -584,6 +590,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("callSiteComplex.1.kt")
public void testCallSiteComplex() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/callSiteComplex.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("exceptionTableSplit.1.kt")
public void testExceptionTableSplit() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt");
@@ -595,6 +607,69 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("finallyInFinally.1.kt")
public void testFinallyInFinally() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/finallyInFinally.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Chained extends AbstractBlackBoxInlineCodegenTest {
public void testAllFilesPresentInChained() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("finallyInFinally.1.kt")
public void testFinallyInFinally() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/finallyInFinally.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("finallyInFinally2.1.kt")
public void testFinallyInFinally2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/finallyInFinally2.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("intReturn.1.kt")
public void testIntReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/intReturn.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("intReturnComplex.1.kt")
public void testIntReturnComplex() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/intReturnComplex.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("intReturnComplex2.1.kt")
public void testIntReturnComplex2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/intReturnComplex2.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("intReturnComplex3.1.kt")
public void testIntReturnComplex3() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/intReturnComplex3.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("intReturnComplex4.1.kt")
public void testIntReturnComplex4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/intReturnComplex4.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("nestedLambda.1.kt")
public void testNestedLambda() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/nestedLambda.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite")
@@ -617,6 +692,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("intReturnComplex.1.kt")
public void testIntReturnComplex() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/intReturnComplex.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("longReturn.1.kt")
public void testLongReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/longReturn.1.kt");
@@ -647,6 +728,18 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("severalInTry.1.kt")
public void testSeveralInTry() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/severalInTry.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("severalInTryComplex.1.kt")
public void testSeveralInTryComplex() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/severalInTryComplex.1.kt");
doTestMultiFileWithInlineCheck(fileName);
}
@TestMetadata("voidInlineFun.1.kt")
public void testVoidInlineFun() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/voidInlineFun.1.kt");
@@ -558,6 +558,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("kt6956.1.kt")
public void testKt6956() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/kt6956.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("kt7273.1.kt")
public void testKt7273() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/kt7273.1.kt");
@@ -584,6 +590,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("callSiteComplex.1.kt")
public void testCallSiteComplex() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/callSiteComplex.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("exceptionTableSplit.1.kt")
public void testExceptionTableSplit() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt");
@@ -595,6 +607,69 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("finallyInFinally.1.kt")
public void testFinallyInFinally() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/finallyInFinally.1.kt");
doBoxTestWithInlineCheck(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Chained extends AbstractCompileKotlinAgainstInlineKotlinTest {
public void testAllFilesPresentInChained() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained"), Pattern.compile("^(.+)\\.1.kt$"), true);
}
@TestMetadata("finallyInFinally.1.kt")
public void testFinallyInFinally() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/finallyInFinally.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("finallyInFinally2.1.kt")
public void testFinallyInFinally2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/finallyInFinally2.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("intReturn.1.kt")
public void testIntReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/intReturn.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("intReturnComplex.1.kt")
public void testIntReturnComplex() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/intReturnComplex.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("intReturnComplex2.1.kt")
public void testIntReturnComplex2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/intReturnComplex2.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("intReturnComplex3.1.kt")
public void testIntReturnComplex3() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/intReturnComplex3.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("intReturnComplex4.1.kt")
public void testIntReturnComplex4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/intReturnComplex4.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("nestedLambda.1.kt")
public void testNestedLambda() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/chained/nestedLambda.1.kt");
doBoxTestWithInlineCheck(fileName);
}
}
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite")
@@ -617,6 +692,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("intReturnComplex.1.kt")
public void testIntReturnComplex() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/intReturnComplex.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("longReturn.1.kt")
public void testLongReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/longReturn.1.kt");
@@ -647,6 +728,18 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("severalInTry.1.kt")
public void testSeveralInTry() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/severalInTry.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("severalInTryComplex.1.kt")
public void testSeveralInTryComplex() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/severalInTryComplex.1.kt");
doBoxTestWithInlineCheck(fileName);
}
@TestMetadata("voidInlineFun.1.kt")
public void testVoidInlineFun() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/voidInlineFun.1.kt");
@@ -26,7 +26,17 @@ public class InlineMarker {
}
//TODO: remove on ABI increment, kept only for compability
@Deprecated
public static void goToTryCatchBlockEnd() {
}
public static void finallyStart(int finallyDeep) {
}
public static void finallyEnd(int finallyDeep) {
}
}