Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.jet.codegen.inline.InternalFinallyBlockInliner Maven / Gradle / Ivy
/*
* 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 com.google.common.base.Objects;
import com.google.common.collect.Lists;
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.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;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.*;
public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
private static class FinallyBlockInfo {
final AbstractInsnNode startIns;
final AbstractInsnNode endInsExclusive;
private FinallyBlockInfo(@NotNull AbstractInsnNode inclusiveStart, @NotNull AbstractInsnNode exclusiveEnd) {
startIns = inclusiveStart;
endInsExclusive = exclusiveEnd;
}
public boolean isEmpty() {
if (!(startIns instanceof LabelNode)) {
return false;
}
AbstractInsnNode end = endInsExclusive;
while (end != startIns && end instanceof LabelNode) {
end = end.getPrevious();
}
return startIns == end;
}
}
public static void processInlineFunFinallyBlocks(@NotNull MethodNode inlineFun, int lambdaTryCatchBlockNodes) {
int index = 0;
List inlineFunTryBlockInfo = new ArrayList();
for (TryCatchBlockNode block : inlineFun.tryCatchBlocks) {
inlineFunTryBlockInfo.add(new TryCatchBlockNodeInfo(block, index++ < lambdaTryCatchBlockNodes));
}
if (hasFinallyBlocks(inlineFunTryBlockInfo)) {
new InternalFinallyBlockInliner(inlineFun, inlineFunTryBlockInfo).processInlineFunFinallyBlocks();
}
}
@NotNull
private final MethodNode inlineFun;
//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 inlineFunTryBlockInfo) {
this.inlineFun = inlineFun;
for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) {
addNewTryCatchNode(block);
}
}
private int initAndGetVarIndexForNonLocalReturnValue() {
//sortTryCatchBlocks();/*TODO maybe remove*/
MaxLocalsCalculator tempCalcNode = new MaxLocalsCalculator(
InlineCodegenUtil.API,
inlineFun.access, inlineFun.desc, null
);
inlineFun.accept(tempCalcNode);
return tempCalcNode.getMaxLocals();
}
private void processInlineFunFinallyBlocks() {
int nextTempNonLocalVarIndex = initAndGetVarIndexForNonLocalReturnValue();
InsnList instructions = inlineFun.instructions;
//As we do finally block code search after non-local return instruction
// we should be sure that all others non-local returns already processed in this finally block.
// So we do instruction processing in reverse order!
AbstractInsnNode curIns = instructions.getLast();
while (curIns != null) {
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)) {
curIns = curIns.getPrevious();
continue;
}
List currentCoveringNodes = getCoveringFromInnermost();
checkCoveringBlocksInvariant(currentCoveringNodes);
if (currentCoveringNodes.isEmpty() ||
currentCoveringNodes.get(currentCoveringNodes.size() - 1).getOnlyCopyNotProcess()) {
curIns = curIns.getPrevious();
continue;
}
AbstractInsnNode instrInsertFinallyBefore = curIns.getPrevious();
AbstractInsnNode nextPrev = instrInsertFinallyBefore.getPrevious();
Type nonLocalReturnType = InlineCodegenUtil.getReturnType(curIns.getOpcode());
//Generally there could be several tryCatch blocks (group) on one code interval (same start and end labels, but maybe different handlers) -
// all of them refer to one try/*catches*/finally or try/catches.
// 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> clustersFromInnermost = InlinePackage.doClustering(currentCoveringNodes);
Iterator> tryCatchBlockIterator = clustersFromInnermost.iterator();
checkClusterInvariant(clustersFromInnermost);
List additionalNodesToSplit = new ArrayList();
while (tryCatchBlockIterator.hasNext()) {
TryBlockCluster clusterToFindFinally = tryCatchBlockIterator.next();
List clusterBlocks = clusterToFindFinally.getBlocks();
TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1);
FinallyBlockInfo finallyInfo = findFinallyBlockBody(nodeWithDefaultHandlerIfExists, getAllTryCatchNodes());
if (finallyInfo == null) continue;
if (nodeWithDefaultHandlerIfExists.getOnlyCopyNotProcess()) {
additionalNodesToSplit.addAll(clusterBlocks);
continue;
}
instructions.resetLabels();
List tryCatchBlockInlinedInFinally = findTryCatchBlocksInlinedInFinally(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();
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 labelsInsideFinally = rememberOriginalLabelNodes(finallyInfo);
//Writing finally block body to temporary node
AbstractInsnNode currentIns = finallyInfo.startIns;
while (currentIns != finallyInfo.endInsExclusive) {
boolean isInsOrJumpInsideFinally =
!(currentIns instanceof JumpInsnNode) ||
labelsInsideFinally.contains(((JumpInsnNode) currentIns).label);
copyInstruction(nextTempNonLocalVarIndex, curIns, instrInsertFinallyBefore, nonLocalReturnType, finallyBlockCopy,
currentIns, isInsOrJumpInsideFinally);
currentIns = currentIns.getNext();
}
finallyBlockCopy.visitLabel(newFinallyEnd);
if (generateAloadAstore) {
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nextTempNonLocalVarIndex);
nextTempNonLocalVarIndex += nonLocalReturnType.getSize(); //TODO: do more wise indexing
}
finallyBlockCopy.visitLabel(insertedBlockEnd);
//Copying finally body before non-local return instruction
InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore);
updateExceptionTable(clusterBlocks, newFinallyStart, newFinallyEnd,
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info, additionalNodesToSplit);
}
//skip just inserted finally
curIns = curIns.getPrevious();
while (curIns != null && curIns != nextPrev) {
updateCoveringTryBlocks(curIns);
curIns = curIns.getPrevious();
}
}
substituteTryBlockNodes();
}
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 coveringTryCatchBlocks) {
boolean isWasOnlyLocal = false;
for (TryCatchBlockNodeInfo info : Lists.reverse(coveringTryCatchBlocks)) {
assert !isWasOnlyLocal || info.getOnlyCopyNotProcess();
if (info.getOnlyCopyNotProcess()) {
isWasOnlyLocal = true;
}
}
}
private static void checkClusterInvariant(List> clusters) {
boolean isWasOnlyLocal;
isWasOnlyLocal = false;
for (TryBlockCluster cluster : Lists.reverse(clusters)) {
TryCatchBlockNodeInfo info = cluster.getBlocks().get(0);
assert !isWasOnlyLocal || info.getOnlyCopyNotProcess();
if (info.getOnlyCopyNotProcess()) {
isWasOnlyLocal = true;
}
}
}
@NotNull
private static Set rememberOriginalLabelNodes(@NotNull FinallyBlockInfo finallyInfo) {
Set labelsInsideFinally = new HashSet();
for (AbstractInsnNode currentIns = finallyInfo.startIns; currentIns != finallyInfo.endInsExclusive; currentIns = currentIns.getNext()) {
if (currentIns instanceof LabelNode) {
labelsInsideFinally.add((LabelNode) currentIns);
}
}
return labelsInsideFinally;
}
private void updateExceptionTable(
@NotNull List updatingClusterBlocks,
@NotNull Label newFinallyStart,
@NotNull Label newFinallyEnd,
@NotNull List tryCatchBlockPresentInFinally,
@NotNull Set labelsInsideFinally,
@NotNull LabelNode insertedBlockEnd,
@NotNull List patched
) {
//copy tryCatchFinallies that totally in finally block
List> clusters = InlinePackage.doClustering(tryCatchBlockPresentInFinally);
Map> handler2Cluster = new HashMap>();
for (TryBlockCluster cluster : clusters) {
List clusterBlocks = cluster.getBlocks();
TryCatchBlockNodePosition block0 = clusterBlocks.get(0);
TryCatchPosition clusterPosition = block0.getPosition();
if (clusterPosition == TryCatchPosition.INNER) {
for (TryCatchBlockNodePosition position : clusterBlocks) {
assert clusterPosition == position.getPosition() : "Wrong inner tryCatchBlock structure";
TryCatchBlockNode tryCatchBlockNode = position.getNodeInfo().getNode();
assert inlineFun.instructions.indexOf(tryCatchBlockNode.start) <= inlineFun.instructions.indexOf(tryCatchBlockNode.end);
TryCatchBlockNode additionalTryCatchBlock =
new TryCatchBlockNode((LabelNode) tryCatchBlockNode.start.getLabel().info,
(LabelNode) tryCatchBlockNode.end.getLabel().info,
getNewOrOldLabel(tryCatchBlockNode.handler, labelsInsideFinally),
tryCatchBlockNode.type);
assert inlineFun.instructions.indexOf(additionalTryCatchBlock.start) <= inlineFun.instructions.indexOf(additionalTryCatchBlock.end);
TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, true);
addNewTryCatchNode(newInfo);
}
}
else if (clusterPosition == TryCatchPosition.END) {
TryCatchBlockNodePosition defaultHandler = cluster.getDefaultHandler();
assert defaultHandler != null : "Default handler should be present";
handler2Cluster.put(defaultHandler.getHandler(), cluster);
}
else {
assert clusterPosition == TryCatchPosition.START;
TryCatchBlockNodePosition defaultHandler = cluster.getDefaultHandler();
assert defaultHandler != null : "Default handler should be present";
TryBlockCluster endCluster = handler2Cluster.remove(defaultHandler.getHandler());
assert endCluster != null : "Could find start cluster for " + clusterPosition;
//at this point only external finallies could occurs
//they don't collision with updatingClusterBlocks, but may with external ones on next updateExceptionTable invocation
Iterator startBlockPositions = clusterBlocks.iterator();
for (TryCatchBlockNodePosition endBlockPosition : endCluster.getBlocks()) {
TryCatchBlockNodeInfo startNode = startBlockPositions.next().getNodeInfo();
TryCatchBlockNodeInfo endNode = endBlockPosition.getNodeInfo();
assert Objects.equal(startNode.getType(), endNode.getType()) : "Different handler types : " + startNode.getType() + " " + endNode.getType();
patchTryBlocks((LabelNode) startNode.getStartLabel().getLabel().info, endNode);
}
}
}
if (handler2Cluster.size() == 1) {
TryBlockCluster singleCluster = handler2Cluster.values().iterator().next();
if (singleCluster.getBlocks().get(0).getPosition() == TryCatchPosition.END) {
//Pair that starts on default handler don't added to tryCatchBlockPresentInFinally cause it's out of finally block
//TODO rewrite to clusters
for (TryCatchBlockNodePosition endBlockPosition : singleCluster.getBlocks()) {
TryCatchBlockNodeInfo endNode = endBlockPosition.getNodeInfo();
patchTryBlocks((LabelNode) insertedBlockEnd.getLabel().info, endNode);
//nextPrev = (AbstractInsnNode) insertedBlockEnd.getLabel().info;
}
handler2Cluster.clear();
}
}
assert handler2Cluster.isEmpty() : "Unmatched clusters " + handler2Cluster.size();
List toProcess = new ArrayList();
toProcess.addAll(patched);
toProcess.addAll(updatingClusterBlocks);
patched.clear();
// Inserted finally shouldn't be handled by corresponding catches,
// so we should split original interval by inserted finally one
for (TryCatchBlockNodeInfo block : toProcess) {
//update exception mapping
LabelNode oldStartNode = block.getNode().start;
block.getNode().start = (LabelNode) newFinallyEnd.info;
remapStartLabel(oldStartNode, 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());
addNewTryCatchNode(newInfo);
//TODO add assert
}
sortTryCatchBlocks();
}
private void patchTryBlocks(@NotNull LabelNode newStartLabelNode, @NotNull TryCatchBlockNodeInfo endNode) {
LabelNode oldStart = endNode.getStartLabel();
endNode.getNode().start = newStartLabelNode;
remapStartLabel(oldStart, endNode);
TryCatchBlockNode endTryBlock = endNode.getNode();
TryCatchBlockNode additionalTryCatchBlock =
new TryCatchBlockNode(oldStart,
(LabelNode) endTryBlock.end.getLabel().info,
endTryBlock.handler,
endTryBlock.type);
TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, endNode.getOnlyCopyNotProcess());
addNewTryCatchNode(newInfo);
}
private static LabelNode getNewOrOldLabel(LabelNode oldHandler, @NotNull Set labelsInsideFinally) {
if (labelsInsideFinally.contains(oldHandler)) {
return (LabelNode) oldHandler.getLabel().info;
}
return oldHandler;
}
//Keep information about try blocks that cover current instruction -
// pushing and popping it to stack entering and exiting tryCatchBlock start and end labels
public void updateCoveringTryBlocks(@NotNull AbstractInsnNode curIns) {
super.updateCoveringTryBlocks(curIns, false);
checkCoveringBlocksInvariant(getCoveringFromInnermost());
}
private static boolean hasFinallyBlocks(List inlineFunTryBlockInfo) {
for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) {
if (!block.getOnlyCopyNotProcess() && block.getNode().type == null) {
return true;
}
}
return false;
}
//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:
// "ASTORE defaultHandlerExceptionIndex" (handles itself, as does java).
@Nullable
private FinallyBlockInfo findFinallyBlockBody(
@NotNull TryCatchBlockNodeInfo tryCatchBlock,
@NotNull List 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 sameDefaultHandler = new ArrayList();
LabelNode defaultHandler = null;
boolean afterStartBlock = false;
for (TryCatchBlockNodeInfo block : tryCatchBlocks) {
if (tryCatchBlock == block) {
afterStartBlock = true;
}
if (afterStartBlock) {
if (block.getNode().type == null && (firstLabelInChain(tryCatchBlock.getNode().start) == firstLabelInChain(block.getNode().start) &&
firstLabelInChain(tryCatchBlock.getNode().end) == firstLabelInChain(block.getNode().end)
|| defaultHandler == firstLabelInChain(block.getNode().handler))) {
sameDefaultHandler.add(block); //first is tryCatchBlock if no catch clauses
if (defaultHandler == null) {
defaultHandler = firstLabelInChain(block.getNode().handler);
}
}
}
}
if (sameDefaultHandler.isEmpty()) {
//there is no finally block
//it always should be present in default handler
return null;
}
TryCatchBlockNodeInfo nextIntervalWithSameDefaultHandler = sameDefaultHandler.get(1);
AbstractInsnNode startFinallyChain = tryCatchBlock.getNode().end;
AbstractInsnNode endFinallyChainExclusive = skipLastGotoIfNeeded(nextIntervalWithSameDefaultHandler.getNode().handler,
nextIntervalWithSameDefaultHandler.getNode().start);
FinallyBlockInfo finallyInfo = new FinallyBlockInfo(startFinallyChain.getNext(), endFinallyChainExclusive);
if (inlineFun.instructions.indexOf(finallyInfo.startIns) > inlineFun.instructions.indexOf(finallyInfo.endInsExclusive)) {
throw new AssertionError("Inconsistent finally: block end occurs before start " + traceInterval(finallyInfo.endInsExclusive, finallyInfo.startIns));
}
return finallyInfo;
}
@NotNull
private AbstractInsnNode skipLastGotoIfNeeded(
@NotNull LabelNode defaultHandlerStartLabel,
@NotNull AbstractInsnNode lastFinallyInsExclusive
) {
AbstractInsnNode prevLast = getPrevNoLineNumberOrLabel(lastFinallyInsExclusive, true);
assert prevLast != null : "Empty finally block: " + lastFinallyInsExclusive;
if (prevLast.getOpcode() == Opcodes.GOTO) {
//There we should understand whether goto is jump over catches or last break/continue command inside finally.
//If it's a jump over catches so next is true:
// 1. jump label should go after default catch handler start label
// AND
// 2. it shouldn't be present in default catch block, otherwise it break/continue
LabelNode targetJump = ((JumpInsnNode) prevLast).label;
InsnList instructions = inlineFun.instructions;
if (instructions.indexOf(defaultHandlerStartLabel) < instructions.indexOf(targetJump)) { //1 condition
AbstractInsnNode cur = defaultHandlerStartLabel;
while (cur != targetJump) {
if (cur.getOpcode() == Opcodes.GOTO) {
//noinspection ConstantConditions
if (((JumpInsnNode) cur).label == targetJump) { //fail of 2 condition
return lastFinallyInsExclusive;
}
}
cur = cur.getNext();
}
return prevLast;
}
}
return lastFinallyInsExclusive;
}
@NotNull
private List findTryCatchBlocksInlinedInFinally(@NotNull FinallyBlockInfo finallyInfo) {
List result = new ArrayList();
Map processedBlocks = new HashMap();
for (AbstractInsnNode curInstr = finallyInfo.startIns; curInstr != finallyInfo.endInsExclusive; curInstr = curInstr.getNext()) {
if (!(curInstr instanceof LabelNode)) continue;
LabelNode curLabel = (LabelNode) curInstr;
List 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 endedTryBlocks = getEndNodes(curLabel);
for (TryCatchBlockNodeInfo block : endedTryBlocks) {
TryCatchBlockNodePosition info = processedBlocks.get(block);
if (info != null) {
assert info.getPosition() == TryCatchPosition.START;
info.setPosition(TryCatchPosition.INNER);
}
else {
info = new TryCatchBlockNodePosition(block, TryCatchPosition.END);
processedBlocks.put(block, info);
result.add(info);
}
}
}
return result;
}
private static void substituteReturnValueInFinally(
int nonLocalVarIndex,
@NotNull Type nonLocalReturnType,
@NotNull MethodNode finallyBlockCopy,
@NotNull Type localReturnType,
boolean doPop
) {
if (doPop && localReturnType != Type.VOID_TYPE) {
AsmUtil.pop(finallyBlockCopy, localReturnType);
}
if (nonLocalReturnType != Type.VOID_TYPE) {
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nonLocalVarIndex);
}
}
@Nullable
private static AbstractInsnNode getPrevNoLineNumberOrLabel(@NotNull AbstractInsnNode node, boolean strict) {
AbstractInsnNode result = strict ? node.getPrevious() : node;
while (isLineNumberOrLabel(result)) {
result = result.getPrevious();
}
return result;
}
@Override
public int instructionIndex(@NotNull AbstractInsnNode inst) {
return inlineFun.instructions.indexOf(inst);
}
private void substituteTryBlockNodes() {
inlineFun.tryCatchBlocks.clear();
for (TryCatchBlockNodeInfo info : getNonEmptyNodes()) {
inlineFun.tryCatchBlocks.add(info.getNode());
}
}
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();
}
startNode.accept(visitor);
StringWriter out = new StringWriter();
p.print(new PrintWriter(out));
return out.toString();
}
@SuppressWarnings({"UnusedDeclaration", "UseOfSystemOutOrSystemErr"})
@TestOnly
private void flushCurrentState(@NotNull AbstractInsnNode curNonLocal) {
substituteTryBlockNodes();
System.out.println("Will process instruction at : " + inlineFun.instructions.indexOf(curNonLocal) + " " + curNonLocal.toString());
String text = getNodeText(inlineFun);
System.out.println(text);
}
}