Updated exception table generation: support new exception table in non-local returns
This commit is contained in:
@@ -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 {
|
||||
|
||||
+20
-5
@@ -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 {
|
||||
|
||||
+69
-92
@@ -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 {
|
||||
|
||||
|
||||
Vendored
+94
@@ -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"
|
||||
}
|
||||
Vendored
+13
@@ -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"
|
||||
}
|
||||
}
|
||||
Vendored
+69
@@ -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"
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
package test
|
||||
|
||||
public inline fun doCall(block: ()-> Unit, finallyBlock1: ()-> Unit) {
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
finallyBlock1()
|
||||
}
|
||||
}
|
||||
Vendored
+53
@@ -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"
|
||||
}
|
||||
Vendored
+40
@@ -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()
|
||||
}
|
||||
}
|
||||
Vendored
+53
@@ -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"
|
||||
}
|
||||
Vendored
+38
@@ -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()
|
||||
}
|
||||
}
|
||||
+30
@@ -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"
|
||||
}
|
||||
+32
@@ -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
|
||||
}
|
||||
Vendored
+30
@@ -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"
|
||||
}
|
||||
Vendored
+36
@@ -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
|
||||
}
|
||||
Vendored
+30
@@ -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"
|
||||
}
|
||||
Vendored
+44
@@ -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
|
||||
}
|
||||
Vendored
+34
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
Vendored
+44
@@ -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
|
||||
}
|
||||
Vendored
+34
@@ -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"
|
||||
}
|
||||
|
||||
|
||||
Vendored
+48
@@ -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
|
||||
}
|
||||
+68
@@ -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"
|
||||
}
|
||||
+13
@@ -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"
|
||||
}
|
||||
}
|
||||
Vendored
+127
@@ -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"
|
||||
}
|
||||
Vendored
+39
@@ -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
|
||||
}
|
||||
+75
@@ -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"
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package test
|
||||
|
||||
public inline fun doCall(block: ()-> Unit, block2: ()-> Unit, finallyBlock2: ()-> Unit) {
|
||||
try {
|
||||
block()
|
||||
block2()
|
||||
} finally {
|
||||
finallyBlock2()
|
||||
}
|
||||
}
|
||||
Vendored
+72
@@ -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"
|
||||
}
|
||||
Vendored
+19
@@ -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
|
||||
|
||||
+93
@@ -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");
|
||||
|
||||
+93
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user