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 int myLastLineNumber = -1;
|
||||||
private boolean shouldMarkLineNumbers = true;
|
private boolean shouldMarkLineNumbers = true;
|
||||||
|
private int finallyDeep = 0;
|
||||||
|
|
||||||
public ExpressionCodegen(
|
public ExpressionCodegen(
|
||||||
@NotNull MethodVisitor mv,
|
@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()) {
|
if(!blockStackElements.isEmpty()) {
|
||||||
BlockStackElement stackElement = blockStackElements.peek();
|
BlockStackElement stackElement = blockStackElements.peek();
|
||||||
if (stackElement instanceof FinallyBlockStackElement) {
|
if (stackElement instanceof FinallyBlockStackElement) {
|
||||||
@@ -1790,9 +1791,10 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
private void genFinallyBlockOrGoto(
|
private void genFinallyBlockOrGoto(
|
||||||
@Nullable FinallyBlockStackElement finallyBlockStackElement,
|
@Nullable FinallyBlockStackElement finallyBlockStackElement,
|
||||||
@Nullable Label tryCatchBlockEnd,
|
@Nullable Label tryCatchBlockEnd,
|
||||||
@Nullable Label afterReturnLabel
|
@Nullable Label afterJumpLabel
|
||||||
) {
|
) {
|
||||||
if (finallyBlockStackElement != null) {
|
if (finallyBlockStackElement != null) {
|
||||||
|
finallyDeep++;
|
||||||
assert finallyBlockStackElement.gaps.size() % 2 == 0 : "Finally block gaps are inconsistent";
|
assert finallyBlockStackElement.gaps.size() % 2 == 0 : "Finally block gaps are inconsistent";
|
||||||
|
|
||||||
BlockStackElement topOfStack = blockStackElements.pop();
|
BlockStackElement topOfStack = blockStackElements.pop();
|
||||||
@@ -1802,21 +1804,25 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
Label finallyStart = new Label();
|
Label finallyStart = new Label();
|
||||||
v.mark(finallyStart);
|
v.mark(finallyStart);
|
||||||
finallyBlockStackElement.addGapLabel(finallyStart);
|
finallyBlockStackElement.addGapLabel(finallyStart);
|
||||||
|
if (context.isInlineFunction() || context.isInliningLambda()) {
|
||||||
|
InlineCodegenUtil.generateFinallyMarker(v, finallyDeep, true);
|
||||||
|
}
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
gen(jetTryExpression.getFinallyBlock().getFinalExpression(), Type.VOID_TYPE);
|
gen(jetTryExpression.getFinallyBlock().getFinalExpression(), Type.VOID_TYPE);
|
||||||
|
|
||||||
|
if (context.isInlineFunction() || context.isInliningLambda()) {
|
||||||
|
InlineCodegenUtil.generateFinallyMarker(v, finallyDeep, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tryCatchBlockEnd != null) {
|
if (tryCatchBlockEnd != null) {
|
||||||
if (context.isInlineFunction()) {
|
|
||||||
InlineCodegenUtil.generateGoToTryCatchBlockEndMarker(v);
|
|
||||||
}
|
|
||||||
v.goTo(tryCatchBlockEnd);
|
v.goTo(tryCatchBlockEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finallyBlockStackElement != null) {
|
if (finallyBlockStackElement != null) {
|
||||||
Label finallyEnd = afterReturnLabel != null ? afterReturnLabel : new Label();
|
finallyDeep--;
|
||||||
if (afterReturnLabel == null) {
|
Label finallyEnd = afterJumpLabel != null ? afterJumpLabel : new Label();
|
||||||
|
if (afterJumpLabel == null) {
|
||||||
v.mark(finallyEnd);
|
v.mark(finallyEnd);
|
||||||
}
|
}
|
||||||
finallyBlockStackElement.addGapLabel(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);
|
return new Stack<BlockStackElement>(blockStackElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addBlockStackElementsForNonLocalReturns(@NotNull Stack<BlockStackElement> elements) {
|
public void addBlockStackElementsForNonLocalReturns(@NotNull Stack<BlockStackElement> elements, int finallyDeepIndex) {
|
||||||
blockStackElements.addAll(elements);
|
blockStackElements.addAll(elements);
|
||||||
|
this.finallyDeep = finallyDeepIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NonLocalReturnInfo {
|
private static class NonLocalReturnInfo {
|
||||||
|
|||||||
+20
-5
@@ -63,7 +63,7 @@ public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
|
|||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
result = instructionIndex(t1.startLabel) - instructionIndex(t2.startLabel)
|
result = instructionIndex(t1.startLabel) - instructionIndex(t2.startLabel)
|
||||||
if (result == 0) {
|
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)
|
result = instructionIndex(t1.endLabel) - instructionIndex(t2.endLabel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,13 +123,15 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
|
|||||||
|
|
||||||
fun processCurrent(curIns: LabelNode, directOrder: Boolean) {
|
fun processCurrent(curIns: LabelNode, directOrder: Boolean) {
|
||||||
getInterval(curIns, directOrder).forEach {
|
getInterval(curIns, directOrder).forEach {
|
||||||
val b = currentIntervals.add(it)
|
val added = currentIntervals.add(it)
|
||||||
assert(b, "Wrong interval structure: $curIns, $it")
|
if (!added) {
|
||||||
|
assert(added, "Wrong interval structure: $curIns, $it")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getInterval(curIns, !directOrder).forEach {
|
getInterval(curIns, !directOrder).forEach {
|
||||||
val b = currentIntervals.remove(it)
|
val removed = currentIntervals.remove(it)
|
||||||
assert(b, "Wrong interval structure: $curIns, $it")
|
assert(removed, "Wrong interval structure: $curIns, $it")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +146,19 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
|
|||||||
return splittedPair
|
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)
|
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);
|
DefaultProcessor processor = new DefaultProcessor(intoNode, offsetForFinallyLocalVar);
|
||||||
|
|
||||||
|
int curFinallyDeep = 0;
|
||||||
AbstractInsnNode curInstr = intoNode.instructions.getFirst();
|
AbstractInsnNode curInstr = intoNode.instructions.getFirst();
|
||||||
while (curInstr != null) {
|
while (curInstr != null) {
|
||||||
processor.processInstruction(curInstr, true);
|
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);
|
MethodInliner.PointForExternalFinallyBlocks extension = extensionPoints.get(curInstr);
|
||||||
if (extension != null) {
|
if (extension != null) {
|
||||||
Label start = new Label();
|
Label start = new Label();
|
||||||
//Label end = new Label();
|
|
||||||
|
|
||||||
MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode();
|
MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode();
|
||||||
finallyNode.visitLabel(start);
|
finallyNode.visitLabel(start);
|
||||||
@@ -676,7 +680,7 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
ExpressionCodegen finallyCodegen =
|
ExpressionCodegen finallyCodegen =
|
||||||
new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(),
|
new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(),
|
||||||
codegen.getContext(), codegen.getState(), codegen.getParentCodegen());
|
codegen.getContext(), codegen.getState(), codegen.getParentCodegen());
|
||||||
finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements());
|
finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements(), curFinallyDeep);
|
||||||
|
|
||||||
FrameMap frameMap = finallyCodegen.getFrameMap();
|
FrameMap frameMap = finallyCodegen.getFrameMap();
|
||||||
FrameMap.Mark mark = frameMap.mark();
|
FrameMap.Mark mark = frameMap.mark();
|
||||||
@@ -685,14 +689,14 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType, extension.labelNode.getLabel());
|
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
|
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
|
||||||
InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, curInstr);
|
InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, curInstr);
|
||||||
|
|
||||||
SimpleInterval splitBy = new SimpleInterval((LabelNode) start.info, extension.labelNode);
|
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();
|
mark.dropTo();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
|
|||||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi;
|
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.PackageClassUtils;
|
||||||
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
|
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.ClassId;
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.name.FqName;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
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_CLASS_NAME = "kotlin/jvm/internal/InlineMarker";
|
||||||
public static final String INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall";
|
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_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
|
@Nullable
|
||||||
public static SMAPAndMethodNode getMethodNode(
|
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
|
//marked return could be either non-local or local in case of labeled lambda self-returns
|
||||||
public static boolean isMarkedReturn(@NotNull AbstractInsnNode returnIns) {
|
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();
|
AbstractInsnNode globalFlag = returnIns.getPrevious();
|
||||||
return globalFlag instanceof MethodInsnNode && NON_LOCAL_RETURN.equals(((MethodInsnNode)globalFlag).owner);
|
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);
|
return new MethodNode(API, 0, "fake", "()V", null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isLineNumberOrLabel(@Nullable AbstractInsnNode node) {
|
|
||||||
return node instanceof LineNumberNode || node instanceof LabelNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static LabelNode firstLabelInChain(@NotNull LabelNode node) {
|
public static LabelNode firstLabelInChain(@NotNull LabelNode node) {
|
||||||
LabelNode curNode = node;
|
LabelNode curNode = node;
|
||||||
@@ -405,16 +404,39 @@ public class InlineCodegenUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void generateGoToTryCatchBlockEndMarker(@NotNull InstructionAdapter v) {
|
public static void generateFinallyMarker(@NotNull InstructionAdapter v, int deep, boolean start) {
|
||||||
v.invokestatic(INLINE_MARKER_CLASS_NAME, INLINE_MARKER_GOTO_TRY_CATCH_BLOCK_END, "()V", false);
|
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) {
|
public static boolean isFinallyEnd(@NotNull AbstractInsnNode node) {
|
||||||
if (!(node.getPrevious() instanceof MethodInsnNode)) return false;
|
return isFinallyMarker(node, INLINE_MARKER_FINALLY_END);
|
||||||
MethodInsnNode previous = (MethodInsnNode) node.getPrevious();
|
}
|
||||||
return node.getOpcode() == Opcodes.GOTO &&
|
|
||||||
INLINE_MARKER_CLASS_NAME.equals(previous.owner) &&
|
public static boolean isFinallyStart(@NotNull AbstractInsnNode node) {
|
||||||
INLINE_MARKER_GOTO_TRY_CATCH_BLOCK_END.equals(previous.name);
|
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 {
|
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.Nullable;
|
||||||
import org.jetbrains.annotations.ReadOnly;
|
import org.jetbrains.annotations.ReadOnly;
|
||||||
import org.jetbrains.annotations.TestOnly;
|
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.Label;
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
import org.jetbrains.org.objectweb.asm.Type;
|
||||||
@@ -44,7 +44,10 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
|
|||||||
|
|
||||||
final AbstractInsnNode endInsExclusive;
|
final AbstractInsnNode endInsExclusive;
|
||||||
|
|
||||||
private FinallyBlockInfo(@NotNull AbstractInsnNode inclusiveStart, @NotNull AbstractInsnNode exclusiveEnd) {
|
private FinallyBlockInfo(
|
||||||
|
@NotNull AbstractInsnNode inclusiveStart,
|
||||||
|
@NotNull AbstractInsnNode exclusiveEnd
|
||||||
|
) {
|
||||||
startIns = inclusiveStart;
|
startIns = inclusiveStart;
|
||||||
endInsExclusive = exclusiveEnd;
|
endInsExclusive = exclusiveEnd;
|
||||||
}
|
}
|
||||||
@@ -139,8 +142,9 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AbstractInsnNode markedReturn = curIns;
|
AbstractInsnNode markedReturn = curIns;
|
||||||
final AbstractInsnNode instrInsertFinallyBefore = markedReturn.getPrevious();
|
AbstractInsnNode instrInsertFinallyBefore = markedReturn.getPrevious();
|
||||||
AbstractInsnNode nextPrev = instrInsertFinallyBefore.getPrevious();
|
AbstractInsnNode nextPrev = instrInsertFinallyBefore.getPrevious().getPrevious();
|
||||||
|
LabelNode newFinallyEnd = (LabelNode) markedReturn.getNext();
|
||||||
Type nonLocalReturnType = InlineCodegenUtil.getReturnType(markedReturn.getOpcode());
|
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) -
|
//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.
|
// 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.
|
// 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
|
// 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();
|
Iterator<TryBlockCluster<TryCatchBlockNodeInfo>> tryCatchBlockIterator = clustersFromInnermost.iterator();
|
||||||
|
|
||||||
checkClusterInvariant(clustersFromInnermost);
|
checkClusterInvariant(clustersFromInnermost);
|
||||||
|
|
||||||
List<TryCatchBlockNodeInfo> additionalNodesToSplit = new ArrayList<TryCatchBlockNodeInfo>();
|
int originalDeepIndex = 0;
|
||||||
|
|
||||||
while (tryCatchBlockIterator.hasNext()) {
|
while (tryCatchBlockIterator.hasNext()) {
|
||||||
TryBlockCluster<TryCatchBlockNodeInfo> clusterToFindFinally = tryCatchBlockIterator.next();
|
TryBlockCluster<TryCatchBlockNodeInfo> clusterToFindFinally = tryCatchBlockIterator.next();
|
||||||
List<TryCatchBlockNodeInfo> clusterBlocks = clusterToFindFinally.getBlocks();
|
List<TryCatchBlockNodeInfo> clusterBlocks = clusterToFindFinally.getBlocks();
|
||||||
TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1);
|
TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1);
|
||||||
|
|
||||||
FinallyBlockInfo finallyInfo = findFinallyBlockBody(nodeWithDefaultHandlerIfExists, getTryBlocksMetaInfo().getAllIntervals());
|
|
||||||
if (finallyInfo == null) continue;
|
|
||||||
|
|
||||||
if (nodeWithDefaultHandlerIfExists.getOnlyCopyNotProcess()) {
|
if (nodeWithDefaultHandlerIfExists.getOnlyCopyNotProcess()) {
|
||||||
additionalNodesToSplit.addAll(clusterBlocks);
|
//lambdas finally generated before non-local return instruction,
|
||||||
continue;
|
//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();
|
instructions.resetLabels();
|
||||||
|
|
||||||
List<TryCatchBlockNodePosition> tryCatchBlockInlinedInFinally = findTryCatchBlocksInlinedInFinally(finallyInfo);
|
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
|
//Creating temp node for finally block copy with some additional instruction
|
||||||
MethodNode finallyBlockCopy = createEmptyMethodNode();
|
MethodNode finallyBlockCopy = createEmptyMethodNode();
|
||||||
Label newFinallyStart = new Label();
|
Label newFinallyStart = new Label();
|
||||||
Label newFinallyEnd = new Label();
|
|
||||||
Label insertedBlockEnd = new Label();
|
Label insertedBlockEnd = new Label();
|
||||||
|
|
||||||
boolean generateAloadAstore = nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty();
|
boolean generateAloadAstore = nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty();
|
||||||
@@ -193,13 +204,11 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
|
|||||||
!(currentIns instanceof JumpInsnNode) ||
|
!(currentIns instanceof JumpInsnNode) ||
|
||||||
labelsInsideFinally.contains(((JumpInsnNode) currentIns).label);
|
labelsInsideFinally.contains(((JumpInsnNode) currentIns).label);
|
||||||
|
|
||||||
copyInstruction(nextTempNonLocalVarIndex, markedReturn, instrInsertFinallyBefore, nonLocalReturnType, finallyBlockCopy,
|
copyInstruction(finallyBlockCopy, currentIns, isInsOrJumpInsideFinally, originalDeepIndex);
|
||||||
currentIns, isInsOrJumpInsideFinally);
|
|
||||||
|
|
||||||
currentIns = currentIns.getNext();
|
currentIns = currentIns.getNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
finallyBlockCopy.visitLabel(newFinallyEnd);
|
|
||||||
if (generateAloadAstore) {
|
if (generateAloadAstore) {
|
||||||
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nextTempNonLocalVarIndex);
|
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nextTempNonLocalVarIndex);
|
||||||
nextTempNonLocalVarIndex += nonLocalReturnType.getSize(); //TODO: do more wise indexing
|
nextTempNonLocalVarIndex += nonLocalReturnType.getSize(); //TODO: do more wise indexing
|
||||||
@@ -211,7 +220,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
|
|||||||
InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore);
|
InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore);
|
||||||
|
|
||||||
updateExceptionTable(clusterBlocks, newFinallyStart, newFinallyEnd,
|
updateExceptionTable(clusterBlocks, newFinallyStart, newFinallyEnd,
|
||||||
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info, additionalNodesToSplit);
|
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
//skip just inserted finally
|
//skip just inserted finally
|
||||||
@@ -237,38 +246,22 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void copyInstruction(
|
private static void copyInstruction(
|
||||||
int nextTempNonLocalVarIndex,
|
|
||||||
@NotNull AbstractInsnNode curIns,
|
|
||||||
@NotNull AbstractInsnNode instrInsertFinallyBefore,
|
|
||||||
@NotNull Type nonLocalReturnType,
|
|
||||||
@NotNull MethodNode finallyBlockCopy,
|
@NotNull MethodNode finallyBlockCopy,
|
||||||
@NotNull AbstractInsnNode currentIns,
|
@NotNull AbstractInsnNode currentIns,
|
||||||
boolean isInsOrJumpInsideFinally
|
boolean isInsOrJumpInsideFinally,
|
||||||
|
int deep
|
||||||
) {
|
) {
|
||||||
//This condition allows another model for non-local returns processing
|
if (isInsOrJumpInsideFinally) {
|
||||||
if (false) {
|
if (InlineCodegenUtil.isFinallyMarker(currentIns.getNext())) {
|
||||||
boolean isReturnForSubstitution =
|
Integer constant = getConstant(currentIns);
|
||||||
InlineCodegenUtil.isReturnOpcode(currentIns.getOpcode()) && !InlineCodegenUtil.isMarkedReturn(currentIns);
|
finallyBlockCopy.visitLdcInsn(constant + deep);
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
currentIns.accept(finallyBlockCopy); //VISIT
|
currentIns.accept(finallyBlockCopy); //VISIT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (isInsOrJumpInsideFinally) {
|
//keep original jump: add currentIns clone
|
||||||
currentIns.accept(finallyBlockCopy); //VISIT
|
finallyBlockCopy.instructions.add(new JumpInsnNode(currentIns.getOpcode(), ((JumpInsnNode) currentIns).label));
|
||||||
}
|
|
||||||
else {
|
|
||||||
//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(
|
private void updateExceptionTable(
|
||||||
@NotNull List<TryCatchBlockNodeInfo> updatingClusterBlocks,
|
@NotNull List<TryCatchBlockNodeInfo> updatingClusterBlocks,
|
||||||
@NotNull Label newFinallyStart,
|
@NotNull Label newFinallyStart,
|
||||||
@NotNull Label newFinallyEnd,
|
@NotNull LabelNode newFinallyEnd,
|
||||||
@NotNull List<TryCatchBlockNodePosition> tryCatchBlockPresentInFinally,
|
@NotNull List<TryCatchBlockNodePosition> tryCatchBlockPresentInFinally,
|
||||||
@NotNull Set<LabelNode> labelsInsideFinally,
|
@NotNull Set<LabelNode> labelsInsideFinally,
|
||||||
@NotNull LabelNode insertedBlockEnd,
|
@NotNull LabelNode insertedBlockEnd
|
||||||
@NotNull List<TryCatchBlockNodeInfo> patched
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
//copy tryCatchFinallies that totally in finally block
|
//copy tryCatchFinallies that totally in finally block
|
||||||
@@ -379,7 +371,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
|
|||||||
getTryBlocksMetaInfo()
|
getTryBlocksMetaInfo()
|
||||||
.split(endNode, new SimpleInterval((LabelNode) endNode.getNode().end.getLabel().info,
|
.split(endNode, new SimpleInterval((LabelNode) endNode.getNode().end.getLabel().info,
|
||||||
(LabelNode) insertedBlockEnd.getLabel().info), false);
|
(LabelNode) insertedBlockEnd.getLabel().info), false);
|
||||||
//nextPrev = (AbstractInsnNode) insertedBlockEnd.getLabel().info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handler2Cluster.clear();
|
handler2Cluster.clear();
|
||||||
@@ -387,19 +378,16 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
|
|||||||
}
|
}
|
||||||
assert handler2Cluster.isEmpty() : "Unmatched clusters " + handler2Cluster.size();
|
assert handler2Cluster.isEmpty() : "Unmatched clusters " + handler2Cluster.size();
|
||||||
|
|
||||||
List<TryCatchBlockNodeInfo > toProcess = new ArrayList<TryCatchBlockNodeInfo>();
|
SimpleInterval splitBy = new SimpleInterval((LabelNode) newFinallyStart.info, newFinallyEnd);
|
||||||
toProcess.addAll(patched);
|
|
||||||
toProcess.addAll(updatingClusterBlocks);
|
|
||||||
patched.clear();
|
|
||||||
SimpleInterval splitBy = new SimpleInterval((LabelNode) newFinallyStart.info, (LabelNode) newFinallyEnd.info);
|
|
||||||
// Inserted finally shouldn't be handled by corresponding catches,
|
// Inserted finally shouldn't be handled by corresponding catches,
|
||||||
// so we should split original interval by inserted finally one
|
// so we should split original interval by inserted finally one
|
||||||
for (TryCatchBlockNodeInfo block : toProcess) {
|
for (TryCatchBlockNodeInfo block : updatingClusterBlocks) {
|
||||||
//update exception mapping
|
//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
|
//block patched in split method
|
||||||
assert !block.isEmpty() : "Finally block should be non-empty";
|
assert !block.isEmpty() : "Finally block should be non-empty";
|
||||||
patched.add(block);
|
|
||||||
//TODO add assert
|
//TODO add assert
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,15 +420,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
|
|||||||
@NotNull TryCatchBlockNodeInfo tryCatchBlock,
|
@NotNull TryCatchBlockNodeInfo tryCatchBlock,
|
||||||
@ReadOnly @NotNull List<TryCatchBlockNodeInfo> tryCatchBlocks
|
@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>();
|
List<TryCatchBlockNodeInfo> sameDefaultHandler = new ArrayList<TryCatchBlockNodeInfo>();
|
||||||
LabelNode defaultHandler = null;
|
LabelNode defaultHandler = null;
|
||||||
boolean afterStartBlock = false;
|
boolean afterStartBlock = false;
|
||||||
@@ -469,29 +448,42 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
|
|||||||
|
|
||||||
TryCatchBlockNodeInfo nextIntervalWithSameDefaultHandler = sameDefaultHandler.get(1);
|
TryCatchBlockNodeInfo nextIntervalWithSameDefaultHandler = sameDefaultHandler.get(1);
|
||||||
AbstractInsnNode startFinallyChain = tryCatchBlock.getNode().end;
|
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);
|
FinallyBlockInfo finallyInfo = new FinallyBlockInfo(startFinallyChain.getNext(), endFinallyChainExclusive);
|
||||||
|
|
||||||
if (inlineFun.instructions.indexOf(finallyInfo.startIns) > inlineFun.instructions.indexOf(finallyInfo.endInsExclusive)) {
|
checkFinally(finallyInfo);
|
||||||
throw new AssertionError("Inconsistent finally: block end occurs before start " + traceInterval(finallyInfo.endInsExclusive, finallyInfo.startIns));
|
|
||||||
}
|
|
||||||
|
|
||||||
return finallyInfo;
|
return finallyInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private void checkFinally(FinallyBlockInfo finallyInfo) {
|
||||||
private static AbstractInsnNode skipLastGotoIfNeeded(
|
checkFinally(finallyInfo.startIns, finallyInfo.endInsExclusive);
|
||||||
@NotNull AbstractInsnNode lastFinallyInsExclusive
|
}
|
||||||
) {
|
|
||||||
|
|
||||||
AbstractInsnNode prevLast = getPrevNoLineNumberOrLabel(lastFinallyInsExclusive, true);
|
private void checkFinally(IntervalWithHandler intervalWithHandler) {
|
||||||
assert prevLast != null : "Empty finally block: " + lastFinallyInsExclusive;
|
checkFinally(intervalWithHandler.getStartLabel(), intervalWithHandler.getEndLabel());
|
||||||
|
}
|
||||||
|
|
||||||
if (InlineCodegenUtil.isGoToTryCatchBlockEnd(prevLast)) {
|
private void checkFinally(AbstractInsnNode startIns, AbstractInsnNode endInsExclusive) {
|
||||||
return prevLast.getPrevious();
|
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
|
@NotNull
|
||||||
@@ -529,26 +521,11 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
|
|||||||
return result;
|
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
|
@Nullable
|
||||||
private static AbstractInsnNode getPrevNoLineNumberOrLabel(@NotNull AbstractInsnNode node, boolean strict) {
|
private static AbstractInsnNode getNextMeaningful(@NotNull AbstractInsnNode node) {
|
||||||
AbstractInsnNode result = strict ? node.getPrevious() : node;
|
AbstractInsnNode result = node.getNext();
|
||||||
while (isLineNumberOrLabel(result)) {
|
while (result != null && !CommonPackage.getIsMeaningful(result)) {
|
||||||
result = result.getPrevious();
|
result = result.getNext();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
class InvokeCall {
|
class InvokeCall {
|
||||||
private final int index;
|
private final int index;
|
||||||
public final LambdaInfo lambdaInfo;
|
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.index = index;
|
||||||
this.lambdaInfo = lambdaInfo;
|
this.lambdaInfo = lambdaInfo;
|
||||||
|
this.finallyDeep = finallyDeep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,9 +104,19 @@ public class MethodInliner {
|
|||||||
@NotNull LocalVarRemapper remapper,
|
@NotNull LocalVarRemapper remapper,
|
||||||
boolean remapReturn,
|
boolean remapReturn,
|
||||||
@NotNull LabelOwner labelOwner
|
@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
|
//analyze body
|
||||||
MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node);
|
MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node, finallyDeepShift);
|
||||||
|
|
||||||
//substitute returns with "goto end" instruction to keep non local returns in lambdas
|
//substitute returns with "goto end" instruction to keep non local returns in lambdas
|
||||||
Label end = new Label();
|
Label end = new Label();
|
||||||
@@ -234,7 +244,7 @@ public class MethodInliner {
|
|||||||
mapper);
|
mapper);
|
||||||
|
|
||||||
LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift);
|
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);
|
result.addAllClassesToRemove(lambdaResult);
|
||||||
|
|
||||||
//return value boxing/unboxing
|
//return value boxing/unboxing
|
||||||
@@ -301,7 +311,7 @@ public class MethodInliner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public MethodNode prepareNode(@NotNull MethodNode node) {
|
public MethodNode prepareNode(@NotNull MethodNode node, int finallyDeepShift) {
|
||||||
final int capturedParamsSize = parameters.getCaptured().size();
|
final int capturedParamsSize = parameters.getCaptured().size();
|
||||||
final int realParametersSize = parameters.getReal().size();
|
final int realParametersSize = parameters.getReal().size();
|
||||||
Type[] types = Type.getArgumentTypes(node.desc);
|
Type[] types = Type.getArgumentTypes(node.desc);
|
||||||
@@ -354,13 +364,14 @@ public class MethodInliner {
|
|||||||
node.accept(transformedNode);
|
node.accept(transformedNode);
|
||||||
|
|
||||||
transformCaptured(transformedNode);
|
transformCaptured(transformedNode);
|
||||||
|
transformFinallyDeepIndex(transformedNode, finallyDeepShift);
|
||||||
|
|
||||||
return transformedNode;
|
return transformedNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) {
|
protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node, int finallyDeepShift) {
|
||||||
node = prepareNode(node);
|
node = prepareNode(node, finallyDeepShift);
|
||||||
|
|
||||||
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()) {
|
Analyzer<SourceValue> analyzer = new Analyzer<SourceValue>(new SourceInterpreter()) {
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -395,6 +406,7 @@ public class MethodInliner {
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
boolean awaitClassReification = false;
|
boolean awaitClassReification = false;
|
||||||
|
int currentFinallyDeep = 0;
|
||||||
|
|
||||||
while (cur != null) {
|
while (cur != null) {
|
||||||
Frame<SourceValue> frame = sources[index];
|
Frame<SourceValue> frame = sources[index];
|
||||||
@@ -404,6 +416,11 @@ public class MethodInliner {
|
|||||||
awaitClassReification = true;
|
awaitClassReification = true;
|
||||||
}
|
}
|
||||||
else if (cur.getType() == AbstractInsnNode.METHOD_INSN) {
|
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;
|
MethodInsnNode methodInsnNode = (MethodInsnNode) cur;
|
||||||
String owner = methodInsnNode.owner;
|
String owner = methodInsnNode.owner;
|
||||||
String desc = methodInsnNode.desc;
|
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)) {
|
else if (isAnonymousConstructorCall(owner, name)) {
|
||||||
Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>();
|
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
|
@NotNull
|
||||||
public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) {
|
public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) {
|
||||||
List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>();
|
List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>();
|
||||||
@@ -701,7 +735,8 @@ public class MethodInliner {
|
|||||||
//genetate finally block before nonLocalReturn flag/return/goto
|
//genetate finally block before nonLocalReturn flag/return/goto
|
||||||
LabelNode label = new LabelNode();
|
LabelNode label = new LabelNode();
|
||||||
instructions.insert(insnNode, label);
|
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));
|
label));
|
||||||
}
|
}
|
||||||
insnNode = insnNode.getNext();
|
insnNode = insnNode.getNext();
|
||||||
@@ -709,6 +744,11 @@ public class MethodInliner {
|
|||||||
return result;
|
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
|
//Place to insert finally blocks from try blocks that wraps inline fun call
|
||||||
public static class PointForExternalFinallyBlocks {
|
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"
|
return "fail"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10 TRYCATCHBLOCK
|
// 8 TRYCATCHBLOCK
|
||||||
@@ -24,6 +24,5 @@ fun box() : String {
|
|||||||
}
|
}
|
||||||
return "fail"
|
return "fail"
|
||||||
}
|
}
|
||||||
|
/* 8 + 1 see notSplitedExceptionTable.kt*/
|
||||||
// maybe we should check test data
|
// 9 TRYCATCHBLOCK
|
||||||
// 13 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);
|
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")
|
@TestMetadata("kt7273.1.kt")
|
||||||
public void testKt7273() throws Exception {
|
public void testKt7273() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/kt7273.1.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/kt7273.1.kt");
|
||||||
@@ -584,6 +590,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
|||||||
doTestMultiFileWithInlineCheck(fileName);
|
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")
|
@TestMetadata("exceptionTableSplit.1.kt")
|
||||||
public void testExceptionTableSplit() throws Exception {
|
public void testExceptionTableSplit() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt");
|
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");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt");
|
||||||
doTestMultiFileWithInlineCheck(fileName);
|
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")
|
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite")
|
||||||
@@ -617,6 +692,12 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
|||||||
doTestMultiFileWithInlineCheck(fileName);
|
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")
|
@TestMetadata("longReturn.1.kt")
|
||||||
public void testLongReturn() throws Exception {
|
public void testLongReturn() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/longReturn.1.kt");
|
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);
|
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")
|
@TestMetadata("voidInlineFun.1.kt")
|
||||||
public void testVoidInlineFun() throws Exception {
|
public void testVoidInlineFun() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/voidInlineFun.1.kt");
|
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);
|
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")
|
@TestMetadata("kt7273.1.kt")
|
||||||
public void testKt7273() throws Exception {
|
public void testKt7273() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/kt7273.1.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/kt7273.1.kt");
|
||||||
@@ -584,6 +590,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
|||||||
doBoxTestWithInlineCheck(fileName);
|
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")
|
@TestMetadata("exceptionTableSplit.1.kt")
|
||||||
public void testExceptionTableSplit() throws Exception {
|
public void testExceptionTableSplit() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt");
|
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");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt");
|
||||||
doBoxTestWithInlineCheck(fileName);
|
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")
|
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite")
|
||||||
@@ -617,6 +692,12 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
|||||||
doBoxTestWithInlineCheck(fileName);
|
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")
|
@TestMetadata("longReturn.1.kt")
|
||||||
public void testLongReturn() throws Exception {
|
public void testLongReturn() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/longReturn.1.kt");
|
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);
|
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")
|
@TestMetadata("voidInlineFun.1.kt")
|
||||||
public void testVoidInlineFun() throws Exception {
|
public void testVoidInlineFun() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite/voidInlineFun.1.kt");
|
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 goToTryCatchBlockEnd() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void finallyStart(int finallyDeep) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void finallyEnd(int finallyDeep) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user