Fixes for call site finally generation before non-local returns
This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2010-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.jet.codegen.inline
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.LabelNode
|
||||
import com.google.common.collect.LinkedListMultimap
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import java.util.ArrayList
|
||||
import com.intellij.util.containers.Stack
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import com.google.common.collect.Lists
|
||||
import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode
|
||||
import java.util.Comparator
|
||||
import java.util.Collections
|
||||
|
||||
public abstract class CoveringTryCatchNodeProcessor<T: IntervalWithHandler>() {
|
||||
|
||||
private val tryBlockStarts = LinkedListMultimap.create<LabelNode, T>()
|
||||
|
||||
private val tryBlockEnds = LinkedListMultimap.create<LabelNode, T>()
|
||||
|
||||
public val allTryCatchNodes: ArrayList<T> = arrayListOf()
|
||||
|
||||
private val currentCoveringBlocks: Stack<T> = Stack()
|
||||
|
||||
public val coveringFromInnermost: List<T>
|
||||
get() = currentCoveringBlocks.reverse()
|
||||
|
||||
fun addNewTryCatchNode(newInfo: T) {
|
||||
tryBlockStarts.put(newInfo.startLabel, newInfo)
|
||||
tryBlockEnds.put(newInfo.endLabel, newInfo)
|
||||
allTryCatchNodes.add(newInfo)
|
||||
}
|
||||
|
||||
fun remapStartLabel(oldStart: LabelNode, remapped: T) {
|
||||
tryBlockStarts.remove(oldStart, remapped)
|
||||
tryBlockStarts.put(remapped.startLabel, remapped)
|
||||
}
|
||||
|
||||
public fun getStartNodes(label: LabelNode): List<T> {
|
||||
return tryBlockStarts.get(label)
|
||||
}
|
||||
|
||||
public fun getEndNodes(label: LabelNode): List<T> {
|
||||
return tryBlockEnds.get(label)
|
||||
}
|
||||
|
||||
//Keep information about try blocks that cover current instruction -
|
||||
// pushing and popping it to stack entering and exiting tryCatchBlock start and end labels
|
||||
public open fun updateCoveringTryBlocks(curIns: AbstractInsnNode, directOrder: Boolean) {
|
||||
if (curIns !is LabelNode) return
|
||||
|
||||
val infosToClose = if (!directOrder) getStartNodes(curIns) else getEndNodes(curIns)
|
||||
for (startNode in infosToClose) {
|
||||
val pop = currentCoveringBlocks.pop()
|
||||
//Temporary disabled cause during patched structure of exceptions changed
|
||||
// if (startNode != pop) {
|
||||
// throw RuntimeException("Wrong try-catch structure " + startNode + " " + pop + " " + infosToClose.size())
|
||||
// };
|
||||
}
|
||||
|
||||
//Reversing list order cause we should pop external block before internal one
|
||||
// (originally internal blocks goes before external one, such invariant preserved via sortTryCatchBlocks method)
|
||||
val infoToOpen = if (!directOrder) getEndNodes(curIns) else getStartNodes(curIns)
|
||||
for (info in infoToOpen.reverse()) {
|
||||
currentCoveringBlocks.add(info)
|
||||
}
|
||||
}
|
||||
|
||||
public abstract fun instructionIndex(inst: AbstractInsnNode): Int
|
||||
|
||||
private fun isEmptyInterval(node: T): Boolean {
|
||||
val start = node.startLabel
|
||||
var end: AbstractInsnNode = node.endLabel
|
||||
while (end != start && end is LabelNode) {
|
||||
end = end.getPrevious()
|
||||
}
|
||||
return start == end;
|
||||
}
|
||||
|
||||
public fun getNonEmptyNodes(): List<T> {
|
||||
return allTryCatchNodes.filterNot { isEmptyInterval(it) }
|
||||
}
|
||||
|
||||
public fun sortTryCatchBlocks() {
|
||||
val comp = Comparator {(t1: T, t2: T): Int ->
|
||||
var result = instructionIndex(t1.handler) - instructionIndex(t2.handler)
|
||||
if (result == 0) {
|
||||
result = instructionIndex(t1.startLabel) - instructionIndex(t2.startLabel)
|
||||
if (result == 0) {
|
||||
assert(false, "Error: support multicatch finallies!")
|
||||
result = instructionIndex(t1.endLabel) - instructionIndex(t2.endLabel)
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
Collections.sort<T>(allTryCatchNodes, comp)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class DefaultProcessor(val node: MethodNode) : CoveringTryCatchNodeProcessor<TryCatchBlockNodeWrapper>() {
|
||||
|
||||
{
|
||||
node.tryCatchBlocks.forEach { addNode(it) }
|
||||
}
|
||||
|
||||
fun addNode(node: TryCatchBlockNode) {
|
||||
addNewTryCatchNode(TryCatchBlockNodeWrapper(node))
|
||||
}
|
||||
|
||||
override fun instructionIndex(inst: AbstractInsnNode): Int {
|
||||
return node.instructions.indexOf(inst)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class TryCatchBlockNodeWrapper(val node: TryCatchBlockNode) : IntervalWithHandler {
|
||||
override val startLabel: LabelNode
|
||||
get() = node.start
|
||||
override val endLabel: LabelNode
|
||||
get() = node.end
|
||||
override val handler: LabelNode
|
||||
get() = node.handler
|
||||
override val type: String?
|
||||
get() = node.type
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.jet.codegen.inline;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.codegen.*;
|
||||
@@ -41,17 +42,18 @@ import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature;
|
||||
import org.jetbrains.jet.lang.types.lang.InlineStrategy;
|
||||
import org.jetbrains.jet.lang.types.lang.InlineUtil;
|
||||
import org.jetbrains.jet.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.org.objectweb.asm.Label;
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method;
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.LabelNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
|
||||
import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.jet.codegen.AsmUtil.getMethodAsmFlags;
|
||||
import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive;
|
||||
@@ -239,7 +241,7 @@ public class InlineCodegen implements CallGenerator {
|
||||
}
|
||||
}
|
||||
};
|
||||
List<MethodInliner.ExternalFinallyBlockInfo> infos = MethodInliner.processReturns(adapter, labelOwner, true, null);
|
||||
List<MethodInliner.PointForExternalFinallyBlocks> infos = MethodInliner.processReturns(adapter, labelOwner, true, null);
|
||||
generateAndInsertFinallyBlocks(adapter, infos);
|
||||
|
||||
adapter.accept(new InliningInstructionAdapter(codegen.v));
|
||||
@@ -481,19 +483,62 @@ public class InlineCodegen implements CallGenerator {
|
||||
}
|
||||
|
||||
|
||||
public void generateAndInsertFinallyBlocks(MethodNode intoNode, List<MethodInliner.ExternalFinallyBlockInfo> insertPoints) {
|
||||
public void generateAndInsertFinallyBlocks(MethodNode intoNode, List<MethodInliner.PointForExternalFinallyBlocks> insertPoints) {
|
||||
if (!codegen.hasFinallyBlocks()) return;
|
||||
|
||||
for (MethodInliner.ExternalFinallyBlockInfo insertPoint : insertPoints) {
|
||||
MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode();
|
||||
ExpressionCodegen finallyCodegen =
|
||||
new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(),
|
||||
codegen.getContext(), codegen.getState(), codegen.getParentCodegen());
|
||||
finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements());
|
||||
Map<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks> extensionPoints =
|
||||
new HashMap<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks>();
|
||||
for (MethodInliner.PointForExternalFinallyBlocks insertPoint : insertPoints) {
|
||||
extensionPoints.put(insertPoint.beforeIns, insertPoint);
|
||||
}
|
||||
|
||||
finallyCodegen.generateFinallyBlocksIfNeeded(insertPoint.returnType);
|
||||
DefaultProcessor processor = new DefaultProcessor(intoNode);
|
||||
|
||||
InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, insertPoint.beforeIns);
|
||||
AbstractInsnNode curInstr = intoNode.instructions.getFirst();
|
||||
while (curInstr != null) {
|
||||
processor.updateCoveringTryBlocks(curInstr, true);
|
||||
|
||||
MethodInliner.PointForExternalFinallyBlocks extension = extensionPoints.get(curInstr);
|
||||
if (extension != null) {
|
||||
Label start = new Label();
|
||||
Label end = new Label();
|
||||
|
||||
MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode();
|
||||
finallyNode.visitLabel(start);
|
||||
|
||||
ExpressionCodegen finallyCodegen =
|
||||
new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(),
|
||||
codegen.getContext(), codegen.getState(), codegen.getParentCodegen());
|
||||
finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements());
|
||||
|
||||
finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType);
|
||||
finallyNode.visitLabel(end);
|
||||
|
||||
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
|
||||
InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, curInstr);
|
||||
|
||||
List<TryCatchBlockNodeWrapper> blocks = processor.getCoveringFromInnermost();
|
||||
ListIterator<TryCatchBlockNodeWrapper> iterator = blocks.listIterator(blocks.size());
|
||||
while (iterator.hasPrevious()) {
|
||||
TryCatchBlockNodeWrapper previous = iterator.previous();
|
||||
LabelNode oldStart = previous.getStartLabel();
|
||||
TryCatchBlockNode node = previous.getNode();
|
||||
node.start = (LabelNode) end.info;
|
||||
processor.remapStartLabel(oldStart, previous);
|
||||
|
||||
TryCatchBlockNode additionalNode = new TryCatchBlockNode(oldStart, (LabelNode) start.info, node.handler, node.type);
|
||||
processor.addNode(additionalNode);
|
||||
}
|
||||
}
|
||||
|
||||
curInstr = curInstr.getNext();
|
||||
}
|
||||
|
||||
processor.sortTryCatchBlocks();
|
||||
Iterable<TryCatchBlockNodeWrapper> nodes = processor.getNonEmptyNodes();
|
||||
intoNode.tryCatchBlocks.clear();
|
||||
for (TryCatchBlockNodeWrapper node : nodes) {
|
||||
intoNode.tryCatchBlocks.add(node.getNode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+123
-186
@@ -17,14 +17,14 @@
|
||||
package org.jetbrains.jet.codegen.inline;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.LinkedListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.jet.codegen.AsmUtil;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
import org.jetbrains.org.objectweb.asm.Label;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.tree.*;
|
||||
import org.jetbrains.org.objectweb.asm.util.Textifier;
|
||||
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
|
||||
@@ -35,7 +35,7 @@ import java.util.*;
|
||||
|
||||
import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.*;
|
||||
|
||||
public class InternalFinallyBlockInliner {
|
||||
public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor<TryCatchBlockNodeInfo> {
|
||||
|
||||
private static class FinallyBlockInfo {
|
||||
|
||||
@@ -76,22 +76,18 @@ public class InternalFinallyBlockInliner {
|
||||
@NotNull
|
||||
private final MethodNode inlineFun;
|
||||
|
||||
private final List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo;
|
||||
|
||||
private final ListMultimap<LabelNode, TryCatchBlockNodeInfo> tryBlockStarts = LinkedListMultimap.create();
|
||||
|
||||
private final ListMultimap<LabelNode, TryCatchBlockNodeInfo> tryBlockEnds = LinkedListMultimap.create();
|
||||
|
||||
//lambdaTryCatchBlockNodes is number of TryCatchBlockNodes that was inlined with lambdas into function
|
||||
//due to code generation specific they placed before function TryCatchBlockNodes
|
||||
private InternalFinallyBlockInliner(@NotNull MethodNode inlineFun, List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) {
|
||||
this.inlineFun = inlineFun;
|
||||
this.inlineFunTryBlockInfo = inlineFunTryBlockInfo;
|
||||
for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) {
|
||||
addNewTryCatchNode(block);
|
||||
}
|
||||
}
|
||||
|
||||
private int initAndGetVarIndexForNonLocalReturnValue() {
|
||||
//sortTryCatchBlocks();/*TODO maybe remove*/
|
||||
mapLabelsToTryCatchBlocks();
|
||||
|
||||
MaxLocalsCalculator tempCalcNode = new MaxLocalsCalculator(
|
||||
InlineCodegenUtil.API,
|
||||
@@ -104,7 +100,6 @@ public class InternalFinallyBlockInliner {
|
||||
private void processInlineFunFinallyBlocks() {
|
||||
int nextTempNonLocalVarIndex = initAndGetVarIndexForNonLocalReturnValue();
|
||||
|
||||
Stack<TryCatchBlockNodeInfo> coveringTryCatchBlocks = new Stack<TryCatchBlockNodeInfo>();
|
||||
InsnList instructions = inlineFun.instructions;
|
||||
|
||||
//As we do finally block code search after non-local return instruction
|
||||
@@ -112,13 +107,19 @@ public class InternalFinallyBlockInliner {
|
||||
// So we do instruction processing in reverse order!
|
||||
AbstractInsnNode curIns = instructions.getLast();
|
||||
while (curIns != null) {
|
||||
updateCoveringTryBlocks(coveringTryCatchBlocks, curIns);
|
||||
updateCoveringTryBlocks(curIns);
|
||||
|
||||
//At this point only global return is possible, local one already substituted with: goto endLabel
|
||||
if (!InlineCodegenUtil.isReturnOpcode(curIns.getOpcode()) ||
|
||||
!InlineCodegenUtil.isMarkedReturn(curIns) ||
|
||||
coveringTryCatchBlocks.isEmpty() ||
|
||||
coveringTryCatchBlocks.get(0).getOnlyCopyNotProcess()) {
|
||||
!InlineCodegenUtil.isMarkedReturn(curIns)) {
|
||||
curIns = curIns.getPrevious();
|
||||
continue;
|
||||
}
|
||||
|
||||
List<TryCatchBlockNodeInfo> currentCoveringNodes = getCoveringFromInnermost();
|
||||
checkCoveringBlocksInvariant(currentCoveringNodes);
|
||||
if (currentCoveringNodes.isEmpty() ||
|
||||
currentCoveringNodes.get(currentCoveringNodes.size() - 1).getOnlyCopyNotProcess()) {
|
||||
curIns = curIns.getPrevious();
|
||||
continue;
|
||||
}
|
||||
@@ -132,25 +133,22 @@ public class InternalFinallyBlockInliner {
|
||||
// Each group that corresponds to try/*catches*/finally contains tryCatch block with default handler.
|
||||
// For each such group we should insert corresponding finally before non-local return.
|
||||
// So we split all try blocks on current instructions to groups and process them independently
|
||||
List<TryBlockCluster<TryCatchBlockNodeInfo>> clusters = InlinePackage.doClustering(coveringTryCatchBlocks);
|
||||
ListIterator<TryBlockCluster<TryCatchBlockNodeInfo>> tryCatchBlockIterator = clusters.listIterator(clusters.size());
|
||||
List<TryBlockCluster<TryCatchBlockNodeInfo>> clustersFromInnermost = InlinePackage.doClustering(currentCoveringNodes);
|
||||
Iterator<TryBlockCluster<TryCatchBlockNodeInfo>> tryCatchBlockIterator = clustersFromInnermost.iterator();
|
||||
|
||||
checkClusterInvariant(clusters);
|
||||
checkClusterInvariant(clustersFromInnermost);
|
||||
|
||||
|
||||
//Reverse visiting cause innermost tryCatchBlocks in the end
|
||||
List<TryCatchBlockNodeInfo> patched = new ArrayList<TryCatchBlockNodeInfo>();
|
||||
while (tryCatchBlockIterator.hasPrevious()) {
|
||||
|
||||
TryBlockCluster clusterToFindFinally = tryCatchBlockIterator.previous();
|
||||
List<TryCatchBlockNodeInfo> additionalNodesToSplit = new ArrayList<TryCatchBlockNodeInfo>();
|
||||
while (tryCatchBlockIterator.hasNext()) {
|
||||
TryBlockCluster clusterToFindFinally = tryCatchBlockIterator.next();
|
||||
List<TryCatchBlockNodeInfo> clusterBlocks = clusterToFindFinally.getBlocks();
|
||||
TryCatchBlockNodeInfo originalTryCatchBlock = clusterBlocks.get(0);
|
||||
TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1);
|
||||
|
||||
FinallyBlockInfo finallyInfo = findFinallyBlockBody(originalTryCatchBlock, inlineFunTryBlockInfo);
|
||||
FinallyBlockInfo finallyInfo = findFinallyBlockBody(nodeWithDefaultHandlerIfExists, getAllTryCatchNodes());
|
||||
if (finallyInfo == null) continue;
|
||||
|
||||
if (originalTryCatchBlock.getOnlyCopyNotProcess()) {
|
||||
patched.addAll(clusterBlocks);
|
||||
if (nodeWithDefaultHandlerIfExists.getOnlyCopyNotProcess()) {
|
||||
additionalNodesToSplit.addAll(clusterBlocks);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -158,54 +156,36 @@ public class InternalFinallyBlockInliner {
|
||||
|
||||
List<TryCatchBlockNodePosition> tryCatchBlockInlinedInFinally = findTryCatchBlocksInlinedInFinally(finallyInfo);
|
||||
|
||||
//Keep some information about label nodes, we need it to understand whether it's jump inside finally block or outside
|
||||
// in first case we do call VISIT on instruction otherwise recreating jump instruction (see below)
|
||||
Set<LabelNode> labelsInsideFinally = rememberOriginalLabelNodes(finallyInfo);
|
||||
|
||||
//Creating temp node for finally block copy with some additional instruction
|
||||
MethodNode finallyBlockCopy = createEmptyMethodNode();
|
||||
Label newFinallyStart = new Label();
|
||||
Label newFinallyEnd = new Label();
|
||||
Label insertedBlockEnd = new Label();
|
||||
|
||||
if (nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty()) {
|
||||
boolean generateAloadAstore = nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty();
|
||||
if (generateAloadAstore) {
|
||||
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ISTORE), nextTempNonLocalVarIndex);
|
||||
}
|
||||
finallyBlockCopy.visitLabel(newFinallyStart);
|
||||
|
||||
//Keep some information about label nodes, we need it to understand whether it's jump inside finally block or outside
|
||||
// in first case we do call VISIT on instruction otherwise recreating jump instruction (see below)
|
||||
Set<LabelNode> labelsInsideFinally = rememberOriginalLabelNodes(finallyInfo);
|
||||
//Writing finally block body to temporary node
|
||||
AbstractInsnNode currentIns = finallyInfo.startIns;
|
||||
while (currentIns != finallyInfo.endInsExclusive) {
|
||||
//This condition allows another model for non-local returns processing
|
||||
if (false && InlineCodegenUtil.isReturnOpcode(currentIns.getOpcode()) && !InlineCodegenUtil.isMarkedReturn(currentIns)) {
|
||||
//substitute all local returns in finally finallyInfo with non-local one lambdaFinallyBlocks try finallyInfo
|
||||
//TODO same for jumps
|
||||
Type localReturnType = InlineCodegenUtil.getReturnType(currentIns.getOpcode());
|
||||
substituteReturnValueInFinally(nextTempNonLocalVarIndex, nonLocalReturnType, finallyBlockCopy,
|
||||
localReturnType, true);
|
||||
boolean isInsOrJumpInsideFinally =
|
||||
!(currentIns instanceof JumpInsnNode) ||
|
||||
labelsInsideFinally.contains(((JumpInsnNode) currentIns).label);
|
||||
|
||||
instrInsertFinallyBefore.accept(finallyBlockCopy);
|
||||
curIns.accept(finallyBlockCopy);
|
||||
}
|
||||
else {
|
||||
boolean isInsOrJumpInsideFinally =
|
||||
!(currentIns instanceof JumpInsnNode) ||
|
||||
labelsInsideFinally.contains(((JumpInsnNode) currentIns).label);
|
||||
|
||||
if (isInsOrJumpInsideFinally) {
|
||||
currentIns.accept(finallyBlockCopy); //VISIT
|
||||
}
|
||||
else {
|
||||
//keep original jump: add currentIns clone
|
||||
finallyBlockCopy.instructions.add(new JumpInsnNode(currentIns.getOpcode(), ((JumpInsnNode) currentIns).label));
|
||||
}
|
||||
}
|
||||
copyInstruction(nextTempNonLocalVarIndex, curIns, instrInsertFinallyBefore, nonLocalReturnType, finallyBlockCopy,
|
||||
currentIns, isInsOrJumpInsideFinally);
|
||||
|
||||
currentIns = currentIns.getNext();
|
||||
}
|
||||
|
||||
finallyBlockCopy.visitLabel(newFinallyEnd);
|
||||
if (nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty()) {
|
||||
if (generateAloadAstore) {
|
||||
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nextTempNonLocalVarIndex);
|
||||
nextTempNonLocalVarIndex += nonLocalReturnType.getSize(); //TODO: do more wise indexing
|
||||
}
|
||||
@@ -216,27 +196,59 @@ public class InternalFinallyBlockInliner {
|
||||
InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore);
|
||||
|
||||
updateExceptionTable(clusterBlocks, newFinallyStart, newFinallyEnd,
|
||||
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info, patched);
|
||||
|
||||
|
||||
}
|
||||
|
||||
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info, additionalNodesToSplit);
|
||||
}
|
||||
|
||||
//skip just inserted finally
|
||||
curIns = curIns.getPrevious();
|
||||
while (curIns != null && curIns != nextPrev) {
|
||||
updateCoveringTryBlocks(coveringTryCatchBlocks, curIns);
|
||||
updateCoveringTryBlocks(curIns);
|
||||
curIns = curIns.getPrevious();
|
||||
}
|
||||
//curIns = nextPrev;
|
||||
}
|
||||
|
||||
substitureTryBlockNodes();
|
||||
substituteTryBlockNodes();
|
||||
}
|
||||
|
||||
private void checkCoveringBlocksInvariant(Stack<TryCatchBlockNodeInfo> coveringTryCatchBlocks) {
|
||||
private static void copyInstruction(
|
||||
int nextTempNonLocalVarIndex,
|
||||
@NotNull AbstractInsnNode curIns,
|
||||
@NotNull AbstractInsnNode instrInsertFinallyBefore,
|
||||
@NotNull Type nonLocalReturnType,
|
||||
@NotNull MethodNode finallyBlockCopy,
|
||||
@NotNull AbstractInsnNode currentIns,
|
||||
boolean isInsOrJumpInsideFinally
|
||||
) {
|
||||
//This condition allows another model for non-local returns processing
|
||||
if (false) {
|
||||
boolean isReturnForSubstitution =
|
||||
InlineCodegenUtil.isReturnOpcode(currentIns.getOpcode()) && !InlineCodegenUtil.isMarkedReturn(currentIns);
|
||||
if (!isInsOrJumpInsideFinally || isReturnForSubstitution) {
|
||||
//substitute all local returns and jumps outside finally with non-local return
|
||||
Type localReturnType = InlineCodegenUtil.getReturnType(currentIns.getOpcode());
|
||||
substituteReturnValueInFinally(nextTempNonLocalVarIndex, nonLocalReturnType, finallyBlockCopy,
|
||||
localReturnType, isReturnForSubstitution);
|
||||
|
||||
instrInsertFinallyBefore.accept(finallyBlockCopy);
|
||||
curIns.accept(finallyBlockCopy);
|
||||
} else {
|
||||
currentIns.accept(finallyBlockCopy); //VISIT
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isInsOrJumpInsideFinally) {
|
||||
currentIns.accept(finallyBlockCopy); //VISIT
|
||||
}
|
||||
else {
|
||||
//keep original jump: add currentIns clone
|
||||
finallyBlockCopy.instructions.add(new JumpInsnNode(currentIns.getOpcode(), ((JumpInsnNode) currentIns).label));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkCoveringBlocksInvariant(List<TryCatchBlockNodeInfo> coveringTryCatchBlocks) {
|
||||
boolean isWasOnlyLocal = false;
|
||||
for (TryCatchBlockNodeInfo info : coveringTryCatchBlocks) {
|
||||
for (TryCatchBlockNodeInfo info : Lists.reverse(coveringTryCatchBlocks)) {
|
||||
assert !isWasOnlyLocal || info.getOnlyCopyNotProcess();
|
||||
if (info.getOnlyCopyNotProcess()) {
|
||||
isWasOnlyLocal = true;
|
||||
@@ -244,10 +256,10 @@ public class InternalFinallyBlockInliner {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkClusterInvariant(List<TryBlockCluster<TryCatchBlockNodeInfo>> clusters) {
|
||||
private static void checkClusterInvariant(List<TryBlockCluster<TryCatchBlockNodeInfo>> clusters) {
|
||||
boolean isWasOnlyLocal;
|
||||
isWasOnlyLocal = false;
|
||||
for (TryBlockCluster<TryCatchBlockNodeInfo> cluster : clusters) {
|
||||
for (TryBlockCluster<TryCatchBlockNodeInfo> cluster : Lists.reverse(clusters)) {
|
||||
TryCatchBlockNodeInfo info = cluster.getBlocks().get(0);
|
||||
assert !isWasOnlyLocal || info.getOnlyCopyNotProcess();
|
||||
if (info.getOnlyCopyNotProcess()) {
|
||||
@@ -267,7 +279,6 @@ public class InternalFinallyBlockInliner {
|
||||
return labelsInsideFinally;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private void updateExceptionTable(
|
||||
@NotNull List<TryCatchBlockNodeInfo> updatingClusterBlocks,
|
||||
@NotNull Label newFinallyStart,
|
||||
@@ -303,9 +314,7 @@ public class InternalFinallyBlockInliner {
|
||||
assert inlineFun.instructions.indexOf(additionalTryCatchBlock.start) <= inlineFun.instructions.indexOf(additionalTryCatchBlock.end);
|
||||
|
||||
TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, true);
|
||||
tryBlockStarts.put(newInfo.getStartLabel(), newInfo);
|
||||
tryBlockEnds.put(newInfo.getEndLabel(), newInfo);
|
||||
inlineFunTryBlockInfo.add(newInfo);
|
||||
addNewTryCatchNode(newInfo);
|
||||
}
|
||||
}
|
||||
else if (clusterPosition == TryCatchPosition.END) {
|
||||
@@ -350,7 +359,7 @@ public class InternalFinallyBlockInliner {
|
||||
}
|
||||
assert handler2Cluster.isEmpty() : "Unmatched clusters " + handler2Cluster.size();
|
||||
|
||||
List<TryCatchBlockNodeInfo > toProcess = new ArrayList<TryCatchBlockNodeInfo >();
|
||||
List<TryCatchBlockNodeInfo > toProcess = new ArrayList<TryCatchBlockNodeInfo>();
|
||||
toProcess.addAll(patched);
|
||||
toProcess.addAll(updatingClusterBlocks);
|
||||
patched.clear();
|
||||
@@ -360,33 +369,25 @@ public class InternalFinallyBlockInliner {
|
||||
//update exception mapping
|
||||
LabelNode oldStartNode = block.getNode().start;
|
||||
block.getNode().start = (LabelNode) newFinallyEnd.info;
|
||||
tryBlockStarts.remove(oldStartNode, block);
|
||||
tryBlockStarts.put(block.getStartLabel(), block);
|
||||
remapStartLabel(oldStartNode, block);
|
||||
|
||||
//if (!block.getOnlyCopyNotProcess()) {
|
||||
patched.add(block);
|
||||
//}
|
||||
patched.add(block);
|
||||
|
||||
TryCatchBlockNode additionalTryCatchBlock =
|
||||
new TryCatchBlockNode(oldStartNode, (LabelNode) newFinallyStart.info, block.getNode().handler, block.getNode().type);
|
||||
|
||||
TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, block.getOnlyCopyNotProcess());
|
||||
tryBlockStarts.put(additionalTryCatchBlock.start, newInfo);
|
||||
tryBlockEnds.put(additionalTryCatchBlock.end, newInfo);
|
||||
|
||||
inlineFunTryBlockInfo.add(newInfo);
|
||||
addNewTryCatchNode(newInfo);
|
||||
|
||||
//TODO add assert
|
||||
}
|
||||
sortTryCatchBlocks(inlineFunTryBlockInfo);
|
||||
sortTryCatchBlocks();
|
||||
}
|
||||
|
||||
private void patchTryBlocks(@NotNull LabelNode newStartLabelNode, @NotNull TryCatchBlockNodeInfo endNode) {
|
||||
LabelNode oldStart = endNode.getStartLabel();
|
||||
endNode.getNode().start = newStartLabelNode;
|
||||
tryBlockStarts.remove(oldStart, endNode);
|
||||
tryBlockStarts.put(endNode.getNode().start, endNode);
|
||||
|
||||
remapStartLabel(oldStart, endNode);
|
||||
|
||||
TryCatchBlockNode endTryBlock = endNode.getNode();
|
||||
TryCatchBlockNode additionalTryCatchBlock =
|
||||
@@ -396,10 +397,7 @@ public class InternalFinallyBlockInliner {
|
||||
endTryBlock.type);
|
||||
|
||||
TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, endNode.getOnlyCopyNotProcess());
|
||||
tryBlockStarts.put(newInfo.getStartLabel(), newInfo);
|
||||
tryBlockEnds.put(newInfo.getEndLabel(), newInfo);
|
||||
|
||||
inlineFunTryBlockInfo.add(newInfo);
|
||||
addNewTryCatchNode(newInfo);
|
||||
}
|
||||
|
||||
private static LabelNode getNewOrOldLabel(LabelNode oldHandler, @NotNull Set<LabelNode> labelsInsideFinally) {
|
||||
@@ -412,23 +410,9 @@ public class InternalFinallyBlockInliner {
|
||||
|
||||
//Keep information about try blocks that cover current instruction -
|
||||
// pushing and popping it to stack entering and exiting tryCatchBlock start and end labels
|
||||
private void updateCoveringTryBlocks(Stack<TryCatchBlockNodeInfo> coveringTryBlocks, AbstractInsnNode curIns) {
|
||||
if (!(curIns instanceof LabelNode)) return;
|
||||
|
||||
List<TryCatchBlockNodeInfo> infos = tryBlockStarts.get((LabelNode) curIns);
|
||||
for (TryCatchBlockNodeInfo startNode : infos) {
|
||||
TryCatchBlockNodeInfo pop = coveringTryBlocks.pop();
|
||||
//Temporary disabled cause during patched structure of exceptions changed
|
||||
//assert startNode == pop : "Wrong try-catch structure " + startNode + " " + pop + " " + infos.size();
|
||||
}
|
||||
|
||||
//Reversing list order cause we should pop external block before internal one
|
||||
// (originally internal blocks goes before external one, such invariant preserved via sortTryCatchBlocks method)
|
||||
for (TryCatchBlockNodeInfo info : Lists.reverse(tryBlockEnds.get((LabelNode) curIns))) {
|
||||
coveringTryBlocks.add(info);
|
||||
}
|
||||
|
||||
checkCoveringBlocksInvariant(coveringTryBlocks);
|
||||
public void updateCoveringTryBlocks(@NotNull AbstractInsnNode curIns) {
|
||||
super.updateCoveringTryBlocks(curIns, false);
|
||||
checkCoveringBlocksInvariant(getCoveringFromInnermost());
|
||||
}
|
||||
|
||||
private static boolean hasFinallyBlocks(List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) {
|
||||
@@ -440,13 +424,6 @@ public class InternalFinallyBlockInliner {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void mapLabelsToTryCatchBlocks() {
|
||||
for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) {
|
||||
tryBlockStarts.put(block.getNode().start, block);
|
||||
tryBlockEnds.put(block.getNode().end, block);
|
||||
}
|
||||
}
|
||||
|
||||
//As described above all tryCatch group that have finally block also should contains tryCatchBlockNode with default handler.
|
||||
//So we assume that instructions between end of tryCatchBlock and start of next tryCatchBlock with same default handler is required finally body.
|
||||
//There is at least two tryCatchBlockNodes in list cause there is always tryCatchBlockNode on first instruction of default handler:
|
||||
@@ -499,10 +476,7 @@ public class InternalFinallyBlockInliner {
|
||||
FinallyBlockInfo finallyInfo = new FinallyBlockInfo(startFinallyChain.getNext(), endFinallyChainExclusive);
|
||||
|
||||
if (inlineFun.instructions.indexOf(finallyInfo.startIns) > inlineFun.instructions.indexOf(finallyInfo.endInsExclusive)) {
|
||||
AbstractInsnNode startNode = finallyInfo.endInsExclusive;
|
||||
AbstractInsnNode stopNode = finallyInfo.startIns;
|
||||
writeNodes(startNode, stopNode);
|
||||
throw new AssertionError();
|
||||
throw new AssertionError("Inconsistent finally: block end occurs before start " + traceInterval(finallyInfo.endInsExclusive, finallyInfo.startIns));
|
||||
}
|
||||
|
||||
return finallyInfo;
|
||||
@@ -553,18 +527,15 @@ public class InternalFinallyBlockInliner {
|
||||
if (!(curInstr instanceof LabelNode)) continue;
|
||||
|
||||
LabelNode curLabel = (LabelNode) curInstr;
|
||||
List<TryCatchBlockNodeInfo> startedTryBlocks = tryBlockStarts.get(curLabel);
|
||||
if (startedTryBlocks != null) {
|
||||
for (TryCatchBlockNodeInfo block : startedTryBlocks) {
|
||||
assert !processedBlocks.containsKey(block) : "Try catch block already processed before start label!!! " + block;
|
||||
TryCatchBlockNodePosition info = new TryCatchBlockNodePosition(block, TryCatchPosition.START);
|
||||
processedBlocks.put(block, info);
|
||||
result.add(info);
|
||||
}
|
||||
List<TryCatchBlockNodeInfo> startedTryBlocks = getStartNodes(curLabel);
|
||||
for (TryCatchBlockNodeInfo block : startedTryBlocks) {
|
||||
assert !processedBlocks.containsKey(block) : "Try catch block already processed before start label!!! " + block;
|
||||
TryCatchBlockNodePosition info = new TryCatchBlockNodePosition(block, TryCatchPosition.START);
|
||||
processedBlocks.put(block, info);
|
||||
result.add(info);
|
||||
}
|
||||
|
||||
List<TryCatchBlockNodeInfo> endedTryBlocks = tryBlockEnds.get(curLabel);
|
||||
if (endedTryBlocks == null) continue;
|
||||
List<TryCatchBlockNodeInfo> endedTryBlocks = getEndNodes(curLabel);
|
||||
|
||||
for (TryCatchBlockNodeInfo block : endedTryBlocks) {
|
||||
TryCatchBlockNodePosition info = processedBlocks.get(block);
|
||||
@@ -582,19 +553,6 @@ public class InternalFinallyBlockInliner {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void writeNodes(AbstractInsnNode startNode, AbstractInsnNode stopNode) {
|
||||
Textifier p = new Textifier();
|
||||
TraceMethodVisitor visitor = new TraceMethodVisitor(p);
|
||||
while (startNode != stopNode) {
|
||||
startNode.accept(visitor);
|
||||
startNode = startNode.getNext();
|
||||
}
|
||||
startNode.accept(visitor);
|
||||
StringWriter out = new StringWriter();
|
||||
p.print(new PrintWriter(out));
|
||||
System.out.println(out.toString());
|
||||
}
|
||||
|
||||
private static void substituteReturnValueInFinally(
|
||||
int nonLocalVarIndex,
|
||||
@NotNull Type nonLocalReturnType,
|
||||
@@ -619,59 +577,38 @@ public class InternalFinallyBlockInliner {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void sortTryCatchBlocks(@NotNull List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) {
|
||||
Comparator<TryCatchBlockNodeInfo> comp = new Comparator<TryCatchBlockNodeInfo>() {
|
||||
@Override
|
||||
public int compare(@NotNull TryCatchBlockNodeInfo t1, @NotNull TryCatchBlockNodeInfo t2) {
|
||||
int result = inlineFun.instructions.indexOf(t1.getNode().handler) - inlineFun.instructions.indexOf(t2.getNode().handler);
|
||||
if (result == 0) {
|
||||
result = inlineFun.instructions.indexOf(t1.getNode().start) - inlineFun.instructions.indexOf(t2.getNode().start);
|
||||
if (result == 0) {
|
||||
assert false : "Error: support multicatch finallies!";
|
||||
result = inlineFun.instructions.indexOf(t1.getNode().end) - inlineFun.instructions.indexOf(t2.getNode().end);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
Collections.sort(inlineFunTryBlockInfo, comp);
|
||||
@Override
|
||||
public int instructionIndex(@NotNull AbstractInsnNode inst) {
|
||||
return inlineFun.instructions.indexOf(inst);
|
||||
}
|
||||
|
||||
for (TryCatchBlockNodeInfo info : inlineFunTryBlockInfo) {
|
||||
TryCatchBlockNode node = info.getNode();
|
||||
assertNotEmptyTryNode(node);
|
||||
private void substituteTryBlockNodes() {
|
||||
inlineFun.tryCatchBlocks.clear();
|
||||
for (TryCatchBlockNodeInfo info : getNonEmptyNodes()) {
|
||||
inlineFun.tryCatchBlocks.add(info.getNode());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertNotEmptyTryNode(TryCatchBlockNode node) {
|
||||
LabelNode start = node.start;
|
||||
AbstractInsnNode end = node.end;
|
||||
while (end != start && end instanceof LabelNode) {
|
||||
end = end.getPrevious();
|
||||
private static String traceInterval(AbstractInsnNode startNode, AbstractInsnNode stopNode) {
|
||||
Textifier p = new Textifier();
|
||||
TraceMethodVisitor visitor = new TraceMethodVisitor(p);
|
||||
while (startNode != stopNode) {
|
||||
startNode.accept(visitor);
|
||||
startNode = startNode.getNext();
|
||||
}
|
||||
assert start != end;
|
||||
startNode.accept(visitor);
|
||||
StringWriter out = new StringWriter();
|
||||
p.print(new PrintWriter(out));
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private static int counter = 0;
|
||||
|
||||
@SuppressWarnings({"UnusedDeclaration", "UseOfSystemOutOrSystemErr"})
|
||||
@TestOnly
|
||||
private void flushCurrentState(@NotNull AbstractInsnNode curNonLocal) {
|
||||
substitureTryBlockNodes();
|
||||
|
||||
System.out.println( );
|
||||
System.out.println();
|
||||
System.out.println("Iteration: " + counter++);
|
||||
substituteTryBlockNodes();
|
||||
System.out.println("Will process instruction at : " + inlineFun.instructions.indexOf(curNonLocal) + " " + curNonLocal.toString());
|
||||
String text = getNodeText(inlineFun);
|
||||
System.out.println(text);
|
||||
}
|
||||
|
||||
private void substitureTryBlockNodes() {
|
||||
inlineFun.tryCatchBlocks.clear();
|
||||
for (TryCatchBlockNodeInfo info : inlineFunTryBlockInfo) {
|
||||
inlineFun.tryCatchBlocks.add(info.getNode());
|
||||
}
|
||||
}
|
||||
|
||||
private int indexOf(AbstractInsnNode node) {
|
||||
return inlineFun.instructions.indexOf(node) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,11 +586,11 @@ public class MethodInliner {
|
||||
|
||||
@NotNull
|
||||
//process local and global returns (local substituted with goto end-label global kept unchanged)
|
||||
public static List<ExternalFinallyBlockInfo> processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) {
|
||||
public static List<PointForExternalFinallyBlocks> processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) {
|
||||
if (!remapReturn) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ExternalFinallyBlockInfo> result = new ArrayList<ExternalFinallyBlockInfo>();
|
||||
List<PointForExternalFinallyBlocks> result = new ArrayList<PointForExternalFinallyBlocks>();
|
||||
InsnList instructions = node.instructions;
|
||||
AbstractInsnNode insnNode = instructions.getFirst();
|
||||
while (insnNode != null) {
|
||||
@@ -621,7 +621,7 @@ public class MethodInliner {
|
||||
}
|
||||
|
||||
//genetate finally block before nonLocalReturn flag/return/goto
|
||||
result.add(new ExternalFinallyBlockInfo(isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode())
|
||||
result.add(new PointForExternalFinallyBlocks(isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode())
|
||||
));
|
||||
}
|
||||
insnNode = insnNode.getNext();
|
||||
@@ -629,13 +629,14 @@ public class MethodInliner {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class ExternalFinallyBlockInfo {
|
||||
//Place to insert finally blocks from try blocks that wraps inline fun call
|
||||
public static class PointForExternalFinallyBlocks {
|
||||
|
||||
final AbstractInsnNode beforeIns;
|
||||
|
||||
final Type returnType;
|
||||
|
||||
public ExternalFinallyBlockInfo(AbstractInsnNode beforeIns, Type returnType) {
|
||||
public PointForExternalFinallyBlocks(AbstractInsnNode beforeIns, Type returnType) {
|
||||
this.beforeIns = beforeIns;
|
||||
this.returnType = returnType;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class TryCatchBlockNodeInfo(val node: TryCatchBlockNode, val onlyCopyNotProcess:
|
||||
override val endLabel: LabelNode
|
||||
get() = node.end
|
||||
override val handler: LabelNode
|
||||
get() = node.handler!!
|
||||
get() = node.handler
|
||||
override val type: String?
|
||||
get() = node.type
|
||||
}
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
import test.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.fail
|
||||
|
||||
fun test1(): Holder {
|
||||
val h = Holder("")
|
||||
|
||||
try {
|
||||
val internalResult = doCall(h) {
|
||||
h += "in lambda body"
|
||||
return h
|
||||
}
|
||||
}
|
||||
finally {
|
||||
h += "in call site finally"
|
||||
}
|
||||
|
||||
h += "local"
|
||||
return h
|
||||
}
|
||||
|
||||
fun test1Lambda(): Holder {
|
||||
val h = Holder("")
|
||||
|
||||
val internalResult = doCall(h) {
|
||||
try {
|
||||
h += "in lambda body"
|
||||
return h
|
||||
}
|
||||
finally {
|
||||
h += "in lambda finally"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
h += "local"
|
||||
return h
|
||||
}
|
||||
|
||||
fun test2(h: Holder): Holder {
|
||||
try {
|
||||
val internalResult = doCallWithException(h) {
|
||||
h += "in lambda body"
|
||||
return h
|
||||
}
|
||||
}
|
||||
finally {
|
||||
h += "in call site finally"
|
||||
}
|
||||
|
||||
h += "local"
|
||||
return h
|
||||
}
|
||||
|
||||
fun test2Lambda(h: Holder): Holder {
|
||||
|
||||
val internalResult = doCallWithException(h) {
|
||||
try {
|
||||
h += "in lambda body"
|
||||
return h
|
||||
}
|
||||
finally {
|
||||
h += "in lambda finally"
|
||||
}
|
||||
}
|
||||
|
||||
h += "local"
|
||||
return h
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val test = test1()
|
||||
if (test.value != "in lambda body -> inline fun finally -> in call site finally") return "fail 1: $test"
|
||||
|
||||
val testLambda = test1Lambda()
|
||||
if (testLambda.value != "in lambda body -> in lambda finally -> inline fun finally") return "fail 1 lambda: $testLambda"
|
||||
|
||||
var h = Holder()
|
||||
assertError(2, h, "in lambda body -> inline fun finally -> in call site finally") {
|
||||
test2(h)
|
||||
}
|
||||
|
||||
h = Holder()
|
||||
assertError(22, h, "in lambda body -> in lambda finally -> inline fun finally") {
|
||||
test2Lambda(h)
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
|
||||
public fun assertError(index: Int, h: Holder, expected: String, l: (h: Holder) -> Holder) {
|
||||
try {
|
||||
l(h)
|
||||
fail("fail $index: no error")
|
||||
}
|
||||
catch (e: Exception) {
|
||||
assertEquals(expected, h.value, "failed on $index")
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package test
|
||||
|
||||
public class Holder(var value: String = "") {
|
||||
|
||||
public fun plusAssign(s: String?) {
|
||||
if (value.length != 0) {
|
||||
value += " -> "
|
||||
}
|
||||
value += s
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public inline fun <R> doCall(h: Holder, block: ()-> R) : R {
|
||||
try {
|
||||
return block()
|
||||
} finally {
|
||||
h += "inline fun finally"
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun <R> doCallWithException(h: Holder, block: ()-> R) : R {
|
||||
try {
|
||||
return block()
|
||||
} finally {
|
||||
h += "inline fun finally"
|
||||
throw RuntimeException("fail");
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
import test.*
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.fail
|
||||
|
||||
fun test1(): Holder {
|
||||
val h = Holder("")
|
||||
|
||||
try {
|
||||
doCall(h) {
|
||||
h += "in lambda body"
|
||||
return h
|
||||
}
|
||||
}
|
||||
finally {
|
||||
h += "in call site finally"
|
||||
}
|
||||
|
||||
h += "local"
|
||||
return h
|
||||
}
|
||||
|
||||
fun test1Lambda(): Holder {
|
||||
val h = Holder("")
|
||||
|
||||
doCall(h) {
|
||||
try {
|
||||
h += "in lambda body"
|
||||
return h
|
||||
}
|
||||
finally {
|
||||
h += "in lambda finally"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
h += "local"
|
||||
return h
|
||||
}
|
||||
|
||||
fun test2(h: Holder): Holder {
|
||||
try {
|
||||
doCallWithException(h) {
|
||||
h += "in lambda body"
|
||||
return h
|
||||
}
|
||||
}
|
||||
finally {
|
||||
h += "in call site finally"
|
||||
}
|
||||
|
||||
h += "local"
|
||||
return h
|
||||
}
|
||||
|
||||
fun test2Lambda(h: Holder): Holder {
|
||||
|
||||
doCallWithException(h) {
|
||||
try {
|
||||
h += "in lambda body"
|
||||
return h
|
||||
}
|
||||
finally {
|
||||
h += "in lambda finally"
|
||||
}
|
||||
}
|
||||
|
||||
h += "local"
|
||||
return h
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val test = test1()
|
||||
if (test.value != "in lambda body -> inline fun finally -> in call site finally") return "fail 1: $test"
|
||||
|
||||
val testLambda = test1Lambda()
|
||||
if (testLambda.value != "in lambda body -> in lambda finally -> inline fun finally") return "fail 1 lambda: $testLambda"
|
||||
|
||||
var h = Holder()
|
||||
assertError(2, h, "in lambda body -> inline fun finally -> in call site finally") {
|
||||
test2(h)
|
||||
}
|
||||
|
||||
h = Holder()
|
||||
assertError(22, h, "in lambda body -> in lambda finally -> inline fun finally") {
|
||||
test2Lambda(h)
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
|
||||
public fun assertError(index: Int, h: Holder, expected: String, l: (h: Holder) -> Holder) {
|
||||
try {
|
||||
l(h)
|
||||
fail("fail $index: no error")
|
||||
}
|
||||
catch (e: Exception) {
|
||||
assertEquals(expected, h.value, "failed on $index")
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package test
|
||||
|
||||
public class Holder(var value: String = "") {
|
||||
|
||||
public fun plusAssign(s: String?) {
|
||||
if (value.length != 0) {
|
||||
value += " -> "
|
||||
}
|
||||
value += s
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public inline fun <R> doCall(h: Holder, block: ()-> R) {
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
h += "inline fun finally"
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun <R> doCallWithException(h: Holder, block: ()-> R) {
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
h += "inline fun finally"
|
||||
throw RuntimeException("fail");
|
||||
}
|
||||
}
|
||||
@@ -25,31 +25,5 @@ fun box() : String {
|
||||
return "fail"
|
||||
}
|
||||
|
||||
// maybe we shouldinline fun test(s: ()->Int): Int {
|
||||
var i = 0;
|
||||
try {
|
||||
i = s()
|
||||
i = i + 10
|
||||
} finally {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
fun box() : String {
|
||||
var p: Int = 1
|
||||
test {
|
||||
try {
|
||||
p = 1
|
||||
return "OK" //finally from inline fun doen't split this try
|
||||
} catch(e: Exception) {
|
||||
p = -1;
|
||||
p
|
||||
} finally {
|
||||
p++
|
||||
}
|
||||
|
||||
}
|
||||
return "fail"
|
||||
}
|
||||
check test data
|
||||
// maybe we should check test data
|
||||
// 13 TRYCATCHBLOCK
|
||||
|
||||
+12
@@ -422,6 +422,18 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxCodegenT
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/callSite.1.kt");
|
||||
doTestMultiFileWithInlineCheck(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("exceptionTableSplit.1.kt")
|
||||
public void testExceptionTableSplit() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt");
|
||||
doTestMultiFileWithInlineCheck(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("exceptionTableSplitNoReturn.1.kt")
|
||||
public void testExceptionTableSplitNoReturn() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt");
|
||||
doTestMultiFileWithInlineCheck(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite")
|
||||
|
||||
+12
@@ -422,6 +422,18 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/callSite.1.kt");
|
||||
doBoxTestWithInlineCheck(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("exceptionTableSplit.1.kt")
|
||||
public void testExceptionTableSplit() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt");
|
||||
doBoxTestWithInlineCheck(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("exceptionTableSplitNoReturn.1.kt")
|
||||
public void testExceptionTableSplitNoReturn() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt");
|
||||
doBoxTestWithInlineCheck(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite")
|
||||
|
||||
Reference in New Issue
Block a user