001 /* 002 * Copyright 2010-2015 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.kotlin.codegen.inline; 018 019 import com.google.common.base.Objects; 020 import com.google.common.collect.Lists; 021 import org.jetbrains.annotations.NotNull; 022 import org.jetbrains.annotations.Nullable; 023 import org.jetbrains.annotations.ReadOnly; 024 import org.jetbrains.annotations.TestOnly; 025 import org.jetbrains.kotlin.codegen.optimization.common.UtilKt; 026 import org.jetbrains.org.objectweb.asm.Label; 027 import org.jetbrains.org.objectweb.asm.Opcodes; 028 import org.jetbrains.org.objectweb.asm.Type; 029 import org.jetbrains.org.objectweb.asm.tree.*; 030 import org.jetbrains.org.objectweb.asm.util.Textifier; 031 import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor; 032 033 import java.io.PrintWriter; 034 import java.io.StringWriter; 035 import java.util.*; 036 037 import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*; 038 039 public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor { 040 041 private static class FinallyBlockInfo { 042 043 final AbstractInsnNode startIns; 044 045 final AbstractInsnNode endInsExclusive; 046 047 private FinallyBlockInfo( 048 @NotNull AbstractInsnNode inclusiveStart, 049 @NotNull AbstractInsnNode exclusiveEnd 050 ) { 051 startIns = inclusiveStart; 052 endInsExclusive = exclusiveEnd; 053 } 054 055 public boolean isEmpty() { 056 if (!(startIns instanceof LabelNode)) { 057 return false; 058 059 } 060 AbstractInsnNode end = endInsExclusive; 061 while (end != startIns && end instanceof LabelNode) { 062 end = end.getPrevious(); 063 } 064 return startIns == end; 065 } 066 } 067 068 public static void processInlineFunFinallyBlocks(@NotNull MethodNode inlineFun, int lambdaTryCatchBlockNodes, int finallyParamOffset) { 069 int index = 0; 070 List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo = new ArrayList<TryCatchBlockNodeInfo>(); 071 for (TryCatchBlockNode block : inlineFun.tryCatchBlocks) { 072 inlineFunTryBlockInfo.add(new TryCatchBlockNodeInfo(block, index++ < lambdaTryCatchBlockNodes)); 073 } 074 075 List<LocalVarNodeWrapper> localVars = new ArrayList<LocalVarNodeWrapper>(); 076 for (LocalVariableNode var : inlineFun.localVariables) { 077 localVars.add(new LocalVarNodeWrapper(var)); 078 } 079 080 if (hasFinallyBlocks(inlineFunTryBlockInfo)) { 081 new InternalFinallyBlockInliner(inlineFun, inlineFunTryBlockInfo, localVars, finallyParamOffset).processInlineFunFinallyBlocks(); 082 } 083 } 084 085 @NotNull 086 private final MethodNode inlineFun; 087 088 089 //lambdaTryCatchBlockNodes is number of TryCatchBlockNodes that was inlined with lambdas into function 090 //due to code generation specific they placed before function TryCatchBlockNodes 091 private InternalFinallyBlockInliner(@NotNull MethodNode inlineFun, 092 @NotNull List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo, 093 @NotNull List<LocalVarNodeWrapper> localVariableInfo, 094 int finallyParamOffset) { 095 super(finallyParamOffset); 096 this.inlineFun = inlineFun; 097 for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) { 098 getTryBlocksMetaInfo().addNewInterval(block); 099 } 100 101 for (LocalVarNodeWrapper wrapper : localVariableInfo) { 102 getLocalVarsMetaInfo().addNewInterval(wrapper); 103 } 104 } 105 106 private int initAndGetVarIndexForNonLocalReturnValue() { 107 MaxLocalsCalculator tempCalcNode = new MaxLocalsCalculator( 108 InlineCodegenUtil.API, 109 inlineFun.access, inlineFun.desc, null 110 ); 111 inlineFun.accept(tempCalcNode); 112 return tempCalcNode.getMaxLocals(); 113 } 114 115 private void processInlineFunFinallyBlocks() { 116 int nextTempNonLocalVarIndex = initAndGetVarIndexForNonLocalReturnValue(); 117 118 InsnList instructions = inlineFun.instructions; 119 120 //As we do finally block code search after non-local return instruction 121 // we should be sure that all others non-local returns already processed in this finally block. 122 // So we do instruction processing in reverse order! 123 AbstractInsnNode curIns = instructions.getLast(); 124 while (curIns != null) { 125 processInstruction(curIns, false); 126 127 //At this point only global return is possible, local one already substituted with: goto endLabel 128 if (!InlineCodegenUtil.isReturnOpcode(curIns.getOpcode()) || 129 !InlineCodegenUtil.isMarkedReturn(curIns)) { 130 curIns = curIns.getPrevious(); 131 continue; 132 } 133 134 List<TryCatchBlockNodeInfo> currentCoveringNodesFromInnermost = 135 sortTryCatchBlocks(new ArrayList<TryCatchBlockNodeInfo>(getTryBlocksMetaInfo().getCurrentIntervals())); 136 checkCoveringBlocksInvariant(Lists.reverse(currentCoveringNodesFromInnermost)); 137 138 if (currentCoveringNodesFromInnermost.isEmpty() || 139 currentCoveringNodesFromInnermost.get(currentCoveringNodesFromInnermost.size() - 1).getOnlyCopyNotProcess()) { 140 curIns = curIns.getPrevious(); 141 continue; 142 } 143 144 AbstractInsnNode markedReturn = curIns; 145 AbstractInsnNode instrInsertFinallyBefore = markedReturn.getPrevious(); 146 AbstractInsnNode nextPrev = instrInsertFinallyBefore.getPrevious(); 147 assert markedReturn.getNext() instanceof LabelNode : "Label should be occurred after non-local return"; 148 LabelNode newFinallyEnd = (LabelNode) markedReturn.getNext(); 149 Type nonLocalReturnType = InlineCodegenUtil.getReturnType(markedReturn.getOpcode()); 150 151 //Generally there could be several tryCatch blocks (group) on one code interval (same start and end labels, but maybe different handlers) - 152 // all of them refer to one try/*catches*/finally or try/catches. 153 // Each group that corresponds to try/*catches*/finally contains tryCatch block with default handler. 154 // For each such group we should insert corresponding finally before non-local return. 155 // So we split all try blocks on current instructions to groups and process them independently 156 List<TryBlockCluster<TryCatchBlockNodeInfo>> clustersFromInnermost = TryBlockClusteringKt.doClustering( 157 currentCoveringNodesFromInnermost); 158 Iterator<TryBlockCluster<TryCatchBlockNodeInfo>> tryCatchBlockIterator = clustersFromInnermost.iterator(); 159 160 checkClusterInvariant(clustersFromInnermost); 161 162 int originalDepthIndex = 0; 163 164 while (tryCatchBlockIterator.hasNext()) { 165 TryBlockCluster<TryCatchBlockNodeInfo> clusterToFindFinally = tryCatchBlockIterator.next(); 166 List<TryCatchBlockNodeInfo> clusterBlocks = clusterToFindFinally.getBlocks(); 167 TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1); 168 169 FinallyBlockInfo finallyInfo = findFinallyBlockBody(nodeWithDefaultHandlerIfExists, getTryBlocksMetaInfo().getAllIntervals()); 170 if (finallyInfo == null) continue; 171 172 if (nodeWithDefaultHandlerIfExists.getOnlyCopyNotProcess()) { 173 //lambdas finally generated before non-local return instruction, 174 //so it's a gap in try/catch handlers 175 throw new RuntimeException("Lambda try blocks should be skipped"); 176 } 177 178 originalDepthIndex++; 179 180 instructions.resetLabels(); 181 182 List<TryCatchBlockNodePosition> tryCatchBlockInlinedInFinally = findTryCatchBlocksInlinedInFinally(finallyInfo); 183 184 //Creating temp node for finally block copy with some additional instruction 185 MethodNode finallyBlockCopy = createEmptyMethodNode(); 186 Label newFinallyStart = new Label(); 187 Label insertedBlockEnd = new Label(); 188 189 boolean generateAloadAstore = nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty(); 190 if (generateAloadAstore) { 191 finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ISTORE), nextTempNonLocalVarIndex); 192 } 193 finallyBlockCopy.visitLabel(newFinallyStart); 194 195 //Keep some information about label nodes, we need it to understand whether it's jump inside finally block or outside 196 // in first case we do call VISIT on instruction otherwise recreating jump instruction (see below) 197 Set<LabelNode> labelsInsideFinally = rememberOriginalLabelNodes(finallyInfo); 198 //Writing finally block body to temporary node 199 AbstractInsnNode currentIns = finallyInfo.startIns; 200 while (currentIns != finallyInfo.endInsExclusive) { 201 boolean isInsOrJumpInsideFinally = 202 !(currentIns instanceof JumpInsnNode) || 203 labelsInsideFinally.contains(((JumpInsnNode) currentIns).label); 204 205 copyInstruction(finallyBlockCopy, currentIns, isInsOrJumpInsideFinally, originalDepthIndex); 206 207 currentIns = currentIns.getNext(); 208 } 209 210 if (generateAloadAstore) { 211 finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nextTempNonLocalVarIndex); 212 nextTempNonLocalVarIndex += nonLocalReturnType.getSize(); //TODO: do more wise indexing 213 } 214 215 finallyBlockCopy.visitLabel(insertedBlockEnd); 216 217 //Copying finally body before non-local return instruction 218 InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore); 219 220 updateExceptionTable(clusterBlocks, newFinallyStart, newFinallyEnd, 221 tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info); 222 } 223 224 //skip just inserted finally 225 curIns = markedReturn.getPrevious(); 226 while (curIns != null && curIns != nextPrev) { 227 processInstruction(curIns, false); 228 curIns = curIns.getPrevious(); 229 } 230 231 //finally block inserted so we need split update localVarTable in lambda 232 if (instrInsertFinallyBefore.getPrevious() != nextPrev && curIns != null) { 233 LabelNode startNode = new LabelNode(); 234 LabelNode endNode = new LabelNode(); 235 instructions.insert(curIns, startNode); 236 //TODO: note that on return expression we have no variables 237 instructions.insert(markedReturn, endNode); 238 getLocalVarsMetaInfo().splitCurrentIntervals(new SimpleInterval(startNode, endNode), true); 239 } 240 } 241 242 substituteTryBlockNodes(inlineFun); 243 substituteLocalVarTable(inlineFun); 244 } 245 246 private static void copyInstruction( 247 @NotNull MethodNode finallyBlockCopy, 248 @NotNull AbstractInsnNode currentIns, 249 boolean isInsOrJumpInsideFinally, 250 int depthShift 251 ) { 252 if (isInsOrJumpInsideFinally) { 253 if (InlineCodegenUtil.isFinallyMarker(currentIns.getNext())) { 254 Integer constant = getConstant(currentIns); 255 finallyBlockCopy.visitLdcInsn(constant + depthShift); 256 } else { 257 currentIns.accept(finallyBlockCopy); //VISIT 258 } 259 } 260 else { 261 //keep original jump: add currentIns clone 262 finallyBlockCopy.instructions.add(new JumpInsnNode(currentIns.getOpcode(), ((JumpInsnNode) currentIns).label)); 263 } 264 } 265 266 private static void checkCoveringBlocksInvariant(@ReadOnly @NotNull List<TryCatchBlockNodeInfo> currentCoveringNodesFromOuterMost) { 267 boolean isWasOnlyLocal = false; 268 for (TryCatchBlockNodeInfo info : currentCoveringNodesFromOuterMost) { 269 assert !isWasOnlyLocal || info.getOnlyCopyNotProcess() : "There are some problems with try-catch structure"; 270 isWasOnlyLocal = info.getOnlyCopyNotProcess(); 271 } 272 } 273 274 private static void checkClusterInvariant(List<TryBlockCluster<TryCatchBlockNodeInfo>> clusters) { 275 boolean isWasOnlyLocal; 276 isWasOnlyLocal = false; 277 for (TryBlockCluster<TryCatchBlockNodeInfo> cluster : Lists.reverse(clusters)) { 278 TryCatchBlockNodeInfo info = cluster.getBlocks().get(0); 279 assert !isWasOnlyLocal || info.getOnlyCopyNotProcess(); 280 if (info.getOnlyCopyNotProcess()) { 281 isWasOnlyLocal = true; 282 } 283 } 284 } 285 286 @NotNull 287 private static Set<LabelNode> rememberOriginalLabelNodes(@NotNull FinallyBlockInfo finallyInfo) { 288 Set<LabelNode> labelsInsideFinally = new HashSet<LabelNode>(); 289 for (AbstractInsnNode currentIns = finallyInfo.startIns; currentIns != finallyInfo.endInsExclusive; currentIns = currentIns.getNext()) { 290 if (currentIns instanceof LabelNode) { 291 labelsInsideFinally.add((LabelNode) currentIns); 292 } 293 } 294 return labelsInsideFinally; 295 } 296 297 private void updateExceptionTable( 298 @NotNull List<TryCatchBlockNodeInfo> updatingClusterBlocks, 299 @NotNull Label newFinallyStart, 300 @NotNull LabelNode newFinallyEnd, 301 @NotNull List<TryCatchBlockNodePosition> tryCatchBlockPresentInFinally, 302 @NotNull Set<LabelNode> labelsInsideFinally, 303 @NotNull LabelNode insertedBlockEnd 304 ) { 305 306 //copy tryCatchFinallies that totally in finally block 307 List<TryBlockCluster<TryCatchBlockNodePosition>> clusters = TryBlockClusteringKt.doClustering(tryCatchBlockPresentInFinally); 308 Map<LabelNode, TryBlockCluster<TryCatchBlockNodePosition>> handler2Cluster = new HashMap<LabelNode, TryBlockCluster<TryCatchBlockNodePosition>>(); 309 310 IntervalMetaInfo<TryCatchBlockNodeInfo> tryBlocksMetaInfo = getTryBlocksMetaInfo(); 311 for (TryBlockCluster<TryCatchBlockNodePosition> cluster : clusters) { 312 List<TryCatchBlockNodePosition> clusterBlocks = cluster.getBlocks(); 313 TryCatchBlockNodePosition block0 = clusterBlocks.get(0); 314 TryCatchPosition clusterPosition = block0.getPosition(); 315 if (clusterPosition == TryCatchPosition.INNER) { 316 for (TryCatchBlockNodePosition position : clusterBlocks) { 317 assert clusterPosition == position.getPosition() : "Wrong inner tryCatchBlock structure"; 318 TryCatchBlockNode tryCatchBlockNode = position.getNodeInfo().getNode(); 319 320 assert inlineFun.instructions.indexOf(tryCatchBlockNode.start) <= inlineFun.instructions.indexOf(tryCatchBlockNode.end); 321 322 TryCatchBlockNode additionalTryCatchBlock = 323 new TryCatchBlockNode((LabelNode) tryCatchBlockNode.start.getLabel().info, 324 (LabelNode) tryCatchBlockNode.end.getLabel().info, 325 getNewOrOldLabel(tryCatchBlockNode.handler, labelsInsideFinally), 326 tryCatchBlockNode.type); 327 328 329 assert inlineFun.instructions.indexOf(additionalTryCatchBlock.start) <= inlineFun.instructions.indexOf(additionalTryCatchBlock.end); 330 331 tryBlocksMetaInfo.addNewInterval(new TryCatchBlockNodeInfo(additionalTryCatchBlock, true)); 332 } 333 } 334 else if (clusterPosition == TryCatchPosition.END) { 335 TryCatchBlockNodePosition defaultHandler = cluster.getDefaultHandler(); 336 assert defaultHandler != null : "Default handler should be present"; 337 handler2Cluster.put(defaultHandler.getHandler(), cluster); 338 } 339 else { 340 assert clusterPosition == TryCatchPosition.START; 341 TryCatchBlockNodePosition defaultHandler = cluster.getDefaultHandler(); 342 assert defaultHandler != null : "Default handler should be present"; 343 TryBlockCluster<TryCatchBlockNodePosition> endCluster = handler2Cluster.remove(defaultHandler.getHandler()); 344 assert endCluster != null : "Could find start cluster for " + clusterPosition; 345 346 //at this point only external finallies could occurs 347 //they don't collision with updatingClusterBlocks, but may with external ones on next updateExceptionTable invocation 348 Iterator<TryCatchBlockNodePosition> startBlockPositions = clusterBlocks.iterator(); 349 for (TryCatchBlockNodePosition endBlockPosition : endCluster.getBlocks()) { 350 TryCatchBlockNodeInfo startNode = startBlockPositions.next().getNodeInfo(); 351 TryCatchBlockNodeInfo endNode = endBlockPosition.getNodeInfo(); 352 353 assert Objects.equal(startNode.getType(), endNode.getType()) : "Different handler types : " + startNode.getType() + " " + endNode.getType(); 354 355 getTryBlocksMetaInfo() 356 .split(endNode, new SimpleInterval((LabelNode) endNode.getNode().end.getLabel().info, 357 (LabelNode) startNode.getStartLabel().getLabel().info), false); 358 } 359 } 360 } 361 362 if (handler2Cluster.size() == 1) { 363 TryBlockCluster<TryCatchBlockNodePosition> singleCluster = handler2Cluster.values().iterator().next(); 364 if (singleCluster.getBlocks().get(0).getPosition() == TryCatchPosition.END) { 365 //Pair that starts on default handler don't added to tryCatchBlockPresentInFinally cause it's out of finally block 366 //TODO rewrite to clusters 367 for (TryCatchBlockNodePosition endBlockPosition : singleCluster.getBlocks()) { 368 TryCatchBlockNodeInfo endNode = endBlockPosition.getNodeInfo(); 369 getTryBlocksMetaInfo() 370 .split(endNode, new SimpleInterval((LabelNode) endNode.getNode().end.getLabel().info, 371 (LabelNode) insertedBlockEnd.getLabel().info), false); 372 } 373 374 handler2Cluster.clear(); 375 } 376 } 377 assert handler2Cluster.isEmpty() : "Unmatched clusters " + handler2Cluster.size(); 378 379 SimpleInterval splitBy = new SimpleInterval((LabelNode) newFinallyStart.info, newFinallyEnd); 380 // Inserted finally shouldn't be handled by corresponding catches, 381 // so we should split original interval by inserted finally one 382 for (TryCatchBlockNodeInfo block : updatingClusterBlocks) { 383 //update exception mapping 384 SplitPair<TryCatchBlockNodeInfo> split = tryBlocksMetaInfo.splitAndRemoveInterval(block, splitBy, false); 385 checkFinally(split.getNewPart()); 386 checkFinally(split.getPatchedPart()); 387 //block patched in split method 388 assert !block.isEmpty() : "Finally block should be non-empty"; 389 //TODO add assert 390 } 391 392 sortTryCatchBlocks(tryBlocksMetaInfo.getAllIntervals()); 393 } 394 395 private static LabelNode getNewOrOldLabel(LabelNode oldHandler, @NotNull Set<LabelNode> labelsInsideFinally) { 396 if (labelsInsideFinally.contains(oldHandler)) { 397 return (LabelNode) oldHandler.getLabel().info; 398 } 399 400 return oldHandler; 401 } 402 403 private static boolean hasFinallyBlocks(List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) { 404 for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) { 405 if (!block.getOnlyCopyNotProcess() && block.getNode().type == null) { 406 return true; 407 } 408 } 409 return false; 410 } 411 412 //As described above all tryCatch group that have finally block also should contains tryCatchBlockNode with default handler. 413 //So we assume that instructions between end of tryCatchBlock and start of next tryCatchBlock with same default handler is required finally body. 414 //There is at least two tryCatchBlockNodes in list cause there is always tryCatchBlockNode on first instruction of default handler: 415 // "ASTORE defaultHandlerExceptionIndex" (handles itself, as does java). 416 @Nullable 417 private FinallyBlockInfo findFinallyBlockBody( 418 @NotNull TryCatchBlockNodeInfo tryCatchBlock, 419 @ReadOnly @NotNull List<TryCatchBlockNodeInfo> tryCatchBlocks 420 ) { 421 List<TryCatchBlockNodeInfo> sameDefaultHandler = new ArrayList<TryCatchBlockNodeInfo>(); 422 LabelNode defaultHandler = null; 423 boolean afterStartBlock = false; 424 for (TryCatchBlockNodeInfo block : tryCatchBlocks) { 425 if (tryCatchBlock == block) { 426 afterStartBlock = true; 427 } 428 429 if (afterStartBlock) { 430 if (block.getNode().type == null && (firstLabelInChain(tryCatchBlock.getNode().start) == firstLabelInChain(block.getNode().start) && 431 firstLabelInChain(tryCatchBlock.getNode().end) == firstLabelInChain(block.getNode().end) 432 || defaultHandler == firstLabelInChain(block.getNode().handler))) { 433 sameDefaultHandler.add(block); //first is tryCatchBlock if no catch clauses 434 if (defaultHandler == null) { 435 defaultHandler = firstLabelInChain(block.getNode().handler); 436 } 437 } 438 } 439 } 440 441 if (sameDefaultHandler.isEmpty()) { 442 //there is no finally block 443 //it always should be present in default handler 444 return null; 445 } 446 447 TryCatchBlockNodeInfo nextIntervalWithSameDefaultHandler = sameDefaultHandler.get(1); 448 AbstractInsnNode startFinallyChain = tryCatchBlock.getNode().end; 449 AbstractInsnNode meaningful = getNextMeaningful(startFinallyChain); 450 assert meaningful != null : "Can't find meaningful in finally block" + startFinallyChain; 451 452 Integer finallyDepth = getConstant(meaningful); 453 AbstractInsnNode endFinallyChainExclusive = nextIntervalWithSameDefaultHandler.getNode().start; 454 AbstractInsnNode current = meaningful.getNext(); 455 while (endFinallyChainExclusive != current) { 456 current = current.getNext(); 457 if (InlineCodegenUtil.isFinallyEnd(current)) { 458 Integer currentDepth = getConstant(current.getPrevious()); 459 if (currentDepth.equals(finallyDepth)) { 460 endFinallyChainExclusive = current.getNext(); 461 break; 462 } 463 } 464 } 465 466 FinallyBlockInfo finallyInfo = new FinallyBlockInfo(startFinallyChain.getNext(), endFinallyChainExclusive); 467 468 checkFinally(finallyInfo); 469 470 return finallyInfo; 471 } 472 473 private void checkFinally(FinallyBlockInfo finallyInfo) { 474 checkFinally(finallyInfo.startIns, finallyInfo.endInsExclusive); 475 } 476 477 private void checkFinally(IntervalWithHandler intervalWithHandler) { 478 checkFinally(intervalWithHandler.getStartLabel(), intervalWithHandler.getEndLabel()); 479 } 480 481 private void checkFinally(AbstractInsnNode startIns, AbstractInsnNode endInsExclusive) { 482 if (inlineFun.instructions.indexOf(startIns) >= inlineFun.instructions.indexOf(endInsExclusive)) { 483 throw new AssertionError("Inconsistent finally: block end occurs before start " + traceInterval(endInsExclusive, startIns)); 484 } 485 } 486 487 @NotNull 488 private List<TryCatchBlockNodePosition> findTryCatchBlocksInlinedInFinally(@NotNull FinallyBlockInfo finallyInfo) { 489 List<TryCatchBlockNodePosition> result = new ArrayList<TryCatchBlockNodePosition>(); 490 Map<TryCatchBlockNodeInfo, TryCatchBlockNodePosition> processedBlocks = new HashMap<TryCatchBlockNodeInfo, TryCatchBlockNodePosition>(); 491 492 for (AbstractInsnNode curInstr = finallyInfo.startIns; curInstr != finallyInfo.endInsExclusive; curInstr = curInstr.getNext()) { 493 if (!(curInstr instanceof LabelNode)) continue; 494 495 LabelNode curLabel = (LabelNode) curInstr; 496 List<TryCatchBlockNodeInfo> startedTryBlocks = getStartNodes(curLabel); 497 for (TryCatchBlockNodeInfo block : startedTryBlocks) { 498 assert !processedBlocks.containsKey(block) : "Try catch block already processed before start label!!! " + block; 499 TryCatchBlockNodePosition info = new TryCatchBlockNodePosition(block, TryCatchPosition.START); 500 processedBlocks.put(block, info); 501 result.add(info); 502 } 503 504 List<TryCatchBlockNodeInfo> endedTryBlocks = getEndNodes(curLabel); 505 506 for (TryCatchBlockNodeInfo block : endedTryBlocks) { 507 TryCatchBlockNodePosition info = processedBlocks.get(block); 508 if (info != null) { 509 assert info.getPosition() == TryCatchPosition.START; 510 info.setPosition(TryCatchPosition.INNER); 511 } 512 else { 513 info = new TryCatchBlockNodePosition(block, TryCatchPosition.END); 514 processedBlocks.put(block, info); 515 result.add(info); 516 } 517 } 518 } 519 return result; 520 } 521 522 @Nullable 523 private static AbstractInsnNode getNextMeaningful(@NotNull AbstractInsnNode node) { 524 AbstractInsnNode result = node.getNext(); 525 while (result != null && !UtilKt.isMeaningful(result)) { 526 result = result.getNext(); 527 } 528 return result; 529 } 530 531 @Override 532 public int instructionIndex(@NotNull AbstractInsnNode inst) { 533 return inlineFun.instructions.indexOf(inst); 534 } 535 536 private static String traceInterval(AbstractInsnNode startNode, AbstractInsnNode stopNode) { 537 Textifier p = new Textifier(); 538 TraceMethodVisitor visitor = new TraceMethodVisitor(p); 539 while (startNode != stopNode) { 540 startNode.accept(visitor); 541 startNode = startNode.getNext(); 542 } 543 startNode.accept(visitor); 544 StringWriter out = new StringWriter(); 545 p.print(new PrintWriter(out)); 546 return out.toString(); 547 } 548 549 @SuppressWarnings({"UnusedDeclaration", "UseOfSystemOutOrSystemErr"}) 550 @TestOnly 551 private void flushCurrentState(@NotNull AbstractInsnNode curNonLocal) { 552 substituteTryBlockNodes(inlineFun); 553 System.out.println("Will process instruction at : " + inlineFun.instructions.indexOf(curNonLocal) + " " + curNonLocal.toString()); 554 String text = getNodeText(inlineFun); 555 System.out.println(text); 556 } 557 558 }