001 /* 002 * Copyright 2010-2014 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.jet.codegen.inline; 018 019 import com.google.common.base.Objects; 020 import com.google.common.collect.LinkedListMultimap; 021 import com.google.common.collect.ListMultimap; 022 import com.google.common.collect.Lists; 023 import com.intellij.util.containers.Stack; 024 import org.jetbrains.annotations.NotNull; 025 import org.jetbrains.annotations.Nullable; 026 import org.jetbrains.jet.codegen.AsmUtil; 027 import org.jetbrains.org.objectweb.asm.*; 028 import org.jetbrains.org.objectweb.asm.tree.*; 029 030 import java.util.*; 031 032 import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.*; 033 034 public class InternalFinallyBlockInliner { 035 036 private static class FinallyBlockInfo { 037 038 final AbstractInsnNode startIns; 039 040 final AbstractInsnNode endInsExclusive; 041 042 private FinallyBlockInfo(@NotNull AbstractInsnNode inclusiveStart, @NotNull AbstractInsnNode exclusiveEnd) { 043 startIns = inclusiveStart; 044 endInsExclusive = exclusiveEnd; 045 } 046 } 047 048 public static void processInlineFunFinallyBlocks(@NotNull MethodNode inlineFun, int lambdaTryCatchBlockNodes) { 049 int index = 0; 050 List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo = new ArrayList<TryCatchBlockNodeInfo>(); 051 for (TryCatchBlockNode block : inlineFun.tryCatchBlocks) { 052 inlineFunTryBlockInfo.add(new TryCatchBlockNodeInfo(block, index++ < lambdaTryCatchBlockNodes)); 053 } 054 055 if (hasFinallyBlocks(inlineFunTryBlockInfo)) { 056 new InternalFinallyBlockInliner(inlineFun, inlineFunTryBlockInfo).processInlineFunFinallyBlocks(); 057 } 058 } 059 060 @NotNull 061 private final MethodNode inlineFun; 062 063 private final List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo; 064 065 private final ListMultimap<LabelNode, TryCatchBlockNodeInfo> tryBlockStarts = LinkedListMultimap.create(); 066 067 private final ListMultimap<LabelNode, TryCatchBlockNodeInfo> tryBlockEnds = LinkedListMultimap.create(); 068 069 //lambdaTryCatchBlockNodes is number of TryCatchBlockNodes that was inlined with lambdas into function 070 //due to code generation specific they placed before function TryCatchBlockNodes 071 private InternalFinallyBlockInliner(@NotNull MethodNode inlineFun, List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) { 072 this.inlineFun = inlineFun; 073 this.inlineFunTryBlockInfo = inlineFunTryBlockInfo; 074 } 075 076 private int initAndGetVarIndexForNonLocalReturnValue() { 077 //sortTryCatchBlocks();/*TODO maybe remove*/ 078 mapLabelsToTryCatchBlocks(); 079 080 MaxLocalsCalculator tempCalcNode = new MaxLocalsCalculator( 081 InlineCodegenUtil.API, 082 inlineFun.access, inlineFun.desc, null 083 ); 084 inlineFun.accept(tempCalcNode); 085 return tempCalcNode.getMaxLocals(); 086 } 087 088 private void processInlineFunFinallyBlocks() { 089 int nextTempNonLocalVarIndex = initAndGetVarIndexForNonLocalReturnValue(); 090 091 Stack<TryCatchBlockNodeInfo> coveringTryCatchBlocks = new Stack<TryCatchBlockNodeInfo>(); 092 InsnList instructions = inlineFun.instructions; 093 094 //As we do finally block code search after non-local return instruction 095 // we should be sure that all others non-local returns already processed in this finally block. 096 // So we do instruction processing in reverse order! 097 AbstractInsnNode curIns = instructions.getLast(); 098 while (curIns != null) { 099 updateCoveringTryBlocks(coveringTryCatchBlocks, curIns); 100 101 //At this point only global return is possible, local one already substituted with: goto endLabel 102 if (!InlineCodegenUtil.isReturnOpcode(curIns.getOpcode()) || !InlineCodegenUtil.isMarkedReturn(curIns)) { 103 curIns = curIns.getPrevious(); 104 continue; 105 } 106 107 AbstractInsnNode instrInsertFinallyBefore = curIns.getPrevious(); 108 AbstractInsnNode nextPrev = instrInsertFinallyBefore.getPrevious(); 109 Type nonLocalReturnType = InlineCodegenUtil.getReturnType(curIns.getOpcode()); 110 111 //Generally there could be several tryCatch blocks (group) on one code interval (same start and end labels, but maybe different handlers) - 112 // all of them refer to one try/*catches*/finally or try/catches. 113 // Each group that corresponds to try/*catches*/finally contains tryCatch block with default handler. 114 // For each such group we should insert corresponding finally before non-local return. 115 // So we split all try blocks on current instructions to groups and process them independently 116 List<TryBlockCluster<TryCatchBlockNodeInfo>> clusters = InlinePackage.doClustering(coveringTryCatchBlocks); 117 ListIterator<TryBlockCluster<TryCatchBlockNodeInfo>> tryCatchBlockIterator = clusters.listIterator(clusters.size()); 118 //Reverse visiting cause innermost tryCatchBlocks in the end 119 while (tryCatchBlockIterator.hasPrevious()) { 120 TryBlockCluster originalFinallyCluster = tryCatchBlockIterator.previous(); 121 List<TryCatchBlockNodeInfo> clusterBlocks = originalFinallyCluster.getBlocks(); 122 TryCatchBlockNodeInfo originalFinallyBlock = clusterBlocks.get(0); 123 124 FinallyBlockInfo finallyInfo = findFinallyBlockBody(originalFinallyBlock, inlineFunTryBlockInfo); 125 if (finallyInfo == null) continue; 126 127 instructions.resetLabels(); 128 129 List<TryCatchBlockNodePosition> tryCatchBlockInlinedInFinally = findTryCatchBlocksInlinedInFinally(finallyInfo); 130 131 //Keep some information about label nodes, we need it to understand whether it's jump inside finally block or outside 132 // in first case we do call VISIT on instruction otherwise recreating jump instruction (see below) 133 Set<LabelNode> labelsInsideFinally = rememberOriginalLabelNodes(finallyInfo); 134 135 //Creating temp node for finally block copy with some additional instruction 136 MethodNode finallyBlockCopy = createEmptyMethodNode(); 137 Label newFinallyStart = new Label(); 138 Label newFinallyEnd = new Label(); 139 Label insertedBlockEnd = new Label(); 140 141 if (nonLocalReturnType != Type.VOID_TYPE) { 142 finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ISTORE), nextTempNonLocalVarIndex); 143 } 144 finallyBlockCopy.visitLabel(newFinallyStart); 145 146 //Writing finally block body to temporary node 147 AbstractInsnNode currentIns = finallyInfo.startIns; 148 while (currentIns != finallyInfo.endInsExclusive) { 149 //This condition allows another model for non-local returns processing 150 if (false && InlineCodegenUtil.isReturnOpcode(currentIns.getOpcode()) && !InlineCodegenUtil.isMarkedReturn(currentIns)) { 151 //substitute all local returns in finally finallyInfo with non-local one lambdaFinallyBlocks try finallyInfo 152 //TODO same for jumps 153 Type localReturnType = InlineCodegenUtil.getReturnType(currentIns.getOpcode()); 154 substituteReturnValueInFinally(nextTempNonLocalVarIndex, nonLocalReturnType, finallyBlockCopy, 155 localReturnType, true); 156 157 instrInsertFinallyBefore.accept(finallyBlockCopy); 158 curIns.accept(finallyBlockCopy); 159 } 160 else { 161 boolean isInsOrJumpInsideFinally = 162 !(currentIns instanceof JumpInsnNode) || 163 labelsInsideFinally.contains(((JumpInsnNode) currentIns).label); 164 165 if (isInsOrJumpInsideFinally) { 166 currentIns.accept(finallyBlockCopy); //VISIT 167 } 168 else { 169 //keep original jump: add currentIns clone 170 finallyBlockCopy.instructions.add(new JumpInsnNode(currentIns.getOpcode(), ((JumpInsnNode) currentIns).label)); 171 } 172 } 173 174 currentIns = currentIns.getNext(); 175 } 176 177 finallyBlockCopy.visitLabel(newFinallyEnd); 178 if (nonLocalReturnType != Type.VOID_TYPE) { 179 finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nextTempNonLocalVarIndex); 180 nextTempNonLocalVarIndex += nonLocalReturnType.getSize(); //TODO: do more wise indexing 181 } 182 183 finallyBlockCopy.visitLabel(insertedBlockEnd); 184 185 //Copying finally body before non-local return instruction 186 InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore); 187 188 nextPrev = updateExceptionTable(coveringTryCatchBlocks, nextPrev, clusterBlocks, newFinallyStart, newFinallyEnd, 189 tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info); 190 } 191 curIns = nextPrev; 192 } 193 194 inlineFun.tryCatchBlocks.clear(); 195 for (TryCatchBlockNodeInfo info : inlineFunTryBlockInfo) { 196 inlineFun.tryCatchBlocks.add(info.getNode()); 197 } 198 } 199 200 @NotNull 201 private static Set<LabelNode> rememberOriginalLabelNodes(@NotNull FinallyBlockInfo finallyInfo) { 202 Set<LabelNode> labelsInsideFinally = new HashSet<LabelNode>(); 203 for (AbstractInsnNode currentIns = finallyInfo.startIns; currentIns != finallyInfo.endInsExclusive; currentIns = currentIns.getNext()) { 204 if (currentIns instanceof LabelNode) { 205 labelsInsideFinally.add((LabelNode) currentIns); 206 } 207 } 208 return labelsInsideFinally; 209 } 210 211 @Nullable 212 private AbstractInsnNode updateExceptionTable( 213 @NotNull Stack<TryCatchBlockNodeInfo> coveringTryBlocks, 214 @Nullable AbstractInsnNode nextPrev, 215 @NotNull List<TryCatchBlockNodeInfo> updatingClusterBlocks, 216 @NotNull Label newFinallyStart, 217 @NotNull Label newFinallyEnd, 218 @NotNull List<TryCatchBlockNodePosition> tryCatchBlockPresentInFinally, 219 @NotNull Set<LabelNode> labelsInsideFinally, 220 @NotNull LabelNode insertedBlockEnd 221 222 ) { 223 224 //copy tryCatchFinallies that totally in finally block 225 List<TryBlockCluster<TryCatchBlockNodePosition>> clusters = InlinePackage.doClustering(tryCatchBlockPresentInFinally); 226 Map<LabelNode, TryBlockCluster<TryCatchBlockNodePosition>> handler2Cluster = new HashMap<LabelNode, TryBlockCluster<TryCatchBlockNodePosition>>(); 227 228 for (TryBlockCluster<TryCatchBlockNodePosition> cluster : clusters) { 229 List<TryCatchBlockNodePosition> clusterBlocks = cluster.getBlocks(); 230 TryCatchBlockNodePosition block0 = clusterBlocks.get(0); 231 TryCatchPosition clusterPosition = block0.getPosition(); 232 if (clusterPosition == TryCatchPosition.INNER) { 233 for (TryCatchBlockNodePosition position : clusterBlocks) { 234 assert clusterPosition == position.getPosition() : "Wrong inner tryCatchBlock structure"; 235 TryCatchBlockNode tryCatchBlockNode = position.getNodeInfo().getNode(); 236 237 assert inlineFun.instructions.indexOf(tryCatchBlockNode.start) <= inlineFun.instructions.indexOf(tryCatchBlockNode.end); 238 239 TryCatchBlockNode additionalTryCatchBlock = 240 new TryCatchBlockNode((LabelNode) tryCatchBlockNode.start.getLabel().info, 241 (LabelNode) tryCatchBlockNode.end.getLabel().info, 242 getNewOrOldLabel(tryCatchBlockNode.handler, labelsInsideFinally), 243 tryCatchBlockNode.type); 244 245 246 assert inlineFun.instructions.indexOf(additionalTryCatchBlock.start) <= inlineFun.instructions.indexOf(additionalTryCatchBlock.end); 247 248 TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, true); 249 tryBlockStarts.put(newInfo.getStartLabel(), newInfo); 250 tryBlockEnds.put(newInfo.getEndLabel(), newInfo); 251 inlineFunTryBlockInfo.add(newInfo); 252 } 253 } 254 else if (clusterPosition == TryCatchPosition.END) { 255 TryCatchBlockNodePosition defaultHandler = cluster.getDefaultHandler(); 256 assert defaultHandler != null : "Default handler should be present"; 257 handler2Cluster.put(defaultHandler.getHandler(), cluster); 258 } 259 else { 260 assert clusterPosition == TryCatchPosition.START; 261 TryCatchBlockNodePosition defaultHandler = cluster.getDefaultHandler(); 262 assert defaultHandler != null : "Default handler should be present"; 263 TryBlockCluster<TryCatchBlockNodePosition> endCluster = handler2Cluster.remove(defaultHandler.getHandler()); 264 assert endCluster != null : "Could find start cluster for " + clusterPosition; 265 266 //at this point only external finallies could occurs 267 //they don't collision with updatingClusterBlocks, but may with external ones on next updateExceptionTable invocation 268 Iterator<TryCatchBlockNodePosition> startBlockPositions = clusterBlocks.iterator(); 269 for (TryCatchBlockNodePosition endBlockPosition : endCluster.getBlocks()) { 270 TryCatchBlockNodeInfo startNode = startBlockPositions.next().getNodeInfo(); 271 TryCatchBlockNodeInfo endNode = endBlockPosition.getNodeInfo(); 272 273 assert Objects.equal(startNode.getType(), endNode.getType()) : "Different handler types : " + startNode.getType() + " " + endNode.getType(); 274 275 patchTryBlocks((LabelNode) startNode.getStartLabel().getLabel().info, endNode, false); 276 } 277 } 278 } 279 280 if (handler2Cluster.size() == 1) { 281 TryBlockCluster<TryCatchBlockNodePosition> singleCluster = handler2Cluster.values().iterator().next(); 282 if (singleCluster.getBlocks().get(0).getPosition() == TryCatchPosition.END) { 283 //Pair that starts on default handler don't added to tryCatchBlockPresentInFinally cause it's out of finally block 284 //TODO rewrite to clusters 285 for (TryCatchBlockNodePosition endBlockPosition : singleCluster.getBlocks()) { 286 TryCatchBlockNodeInfo endNode = endBlockPosition.getNodeInfo(); 287 patchTryBlocks((LabelNode) insertedBlockEnd.getLabel().info, endNode, true); 288 //nextPrev = (AbstractInsnNode) insertedBlockEnd.getLabel().info; 289 } 290 291 handler2Cluster.clear(); 292 } 293 } 294 assert handler2Cluster.isEmpty() : "Unmatched clusters " + handler2Cluster.size(); 295 296 // Inserted finally shouldn't be handled by corresponding catches, 297 // so we should split original interval by inserted finally one 298 for (TryCatchBlockNodeInfo block : updatingClusterBlocks) { 299 //update exception mapping 300 LabelNode oldStartNode = block.getNode().start; 301 tryBlockStarts.remove(oldStartNode, block); 302 block.getNode().start = (LabelNode) newFinallyEnd.info; 303 //tryBlockStarts.put(block.getStartLabel(), block); 304 305 TryCatchBlockNode additionalTryCatchBlock = 306 new TryCatchBlockNode(oldStartNode, (LabelNode) newFinallyStart.info, block.getNode().handler, block.getNode().type); 307 308 TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, false); 309 tryBlockStarts.put(additionalTryCatchBlock.start, newInfo); 310 tryBlockEnds.put(additionalTryCatchBlock.end, newInfo); 311 312 inlineFunTryBlockInfo.add(newInfo); 313 314 //TODO add assert 315 nextPrev = additionalTryCatchBlock.end; 316 coveringTryBlocks.pop(); 317 } 318 sortTryCatchBlocks(inlineFunTryBlockInfo); 319 return nextPrev; 320 } 321 322 private void patchTryBlocks(@NotNull LabelNode newStartLabelNode, @NotNull TryCatchBlockNodeInfo endNode, boolean sort) { 323 LabelNode oldStart = endNode.getStartLabel(); 324 endNode.getNode().start = newStartLabelNode; 325 tryBlockStarts.remove(oldStart, endNode); 326 tryBlockStarts.put(endNode.getNode().start, endNode); 327 328 329 TryCatchBlockNode endTryBlock = endNode.getNode(); 330 TryCatchBlockNode additionalTryCatchBlock = 331 new TryCatchBlockNode(oldStart, 332 (LabelNode) endTryBlock.end.getLabel().info, 333 endTryBlock.handler, 334 endTryBlock.type); 335 336 TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, endNode.getOnlyCopyNotProcess()); 337 tryBlockStarts.put(newInfo.getStartLabel(), newInfo); 338 tryBlockEnds.put(newInfo.getEndLabel(), newInfo); 339 340 inlineFunTryBlockInfo.add(newInfo); 341 } 342 343 private static LabelNode getNewOrOldLabel(LabelNode oldHandler, @NotNull Set<LabelNode> labelsInsideFinally) { 344 if (labelsInsideFinally.contains(oldHandler)) { 345 return (LabelNode) oldHandler.getLabel().info; 346 } 347 348 return oldHandler; 349 } 350 351 //Keep information about try blocks that cover current instruction - 352 // pushing and popping it to stack entering and exiting tryCatchBlock start and end labels 353 private void updateCoveringTryBlocks(Stack<TryCatchBlockNodeInfo> coveringTryBlocks, AbstractInsnNode curIns) { 354 if (!(curIns instanceof LabelNode)) return; 355 356 List<TryCatchBlockNodeInfo> infos = tryBlockStarts.get((LabelNode) curIns); 357 for (TryCatchBlockNodeInfo startNode : infos) { 358 if (!startNode.getOnlyCopyNotProcess()) { 359 TryCatchBlockNodeInfo pop = coveringTryBlocks.pop(); 360 //Temporary disabled cause during patched structure of exceptions changed 361 //assert startNode == pop : "Wrong try-catch structure " + startNode + " " + pop + " " + infos.size(); 362 } 363 } 364 365 //Reversing list order cause we should pop external block before internal one 366 // (originally internal blocks goes before external one, such invariant preserved via sortTryCatchBlocks method) 367 for (TryCatchBlockNodeInfo info : Lists.reverse(tryBlockEnds.get((LabelNode) curIns))) { 368 if (!info.getOnlyCopyNotProcess()) { 369 coveringTryBlocks.add(info); 370 } 371 } 372 } 373 374 private static boolean hasFinallyBlocks(List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) { 375 for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) { 376 if (!block.getOnlyCopyNotProcess() && block.getNode().type == null) { 377 return true; 378 } 379 } 380 return false; 381 } 382 383 private void mapLabelsToTryCatchBlocks() { 384 for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) { 385 tryBlockStarts.put(block.getNode().start, block); 386 tryBlockEnds.put(block.getNode().end, block); 387 } 388 } 389 390 //As described above all tryCatch group that have finally block also should contains tryCatchBlockNode with default handler. 391 //So we assume that instructions between end of tryCatchBlock and start of next tryCatchBlock with same default handler is required finally body. 392 //There is at least two tryCatchBlockNodes in list cause there is always tryCatchBlockNode on first instruction of default handler: 393 // "ASTORE defaultHandlerExceptionIndex" (handles itself, as does java). 394 @Nullable 395 private FinallyBlockInfo findFinallyBlockBody( 396 @NotNull TryCatchBlockNodeInfo tryCatchBlock, 397 @NotNull List<TryCatchBlockNodeInfo> tryCatchBlocks 398 ) { 399 List<TryCatchBlockNodeInfo> sameDefaultHandler = new ArrayList<TryCatchBlockNodeInfo>(); 400 LabelNode defaultHandler = null; 401 boolean afterStartBlock = false; 402 for (TryCatchBlockNodeInfo block : tryCatchBlocks) { 403 if (tryCatchBlock == block) { 404 afterStartBlock = true; 405 } 406 407 if (afterStartBlock) { 408 if (block.getNode().type == null && (firstLabelInChain(tryCatchBlock.getNode().start) == firstLabelInChain(block.getNode().start) && 409 firstLabelInChain(tryCatchBlock.getNode().end) == firstLabelInChain(block.getNode().end) 410 || defaultHandler == firstLabelInChain(block.getNode().handler))) { 411 sameDefaultHandler.add(block); //first is tryCatchBlock if no catch clauses 412 if (defaultHandler == null) { 413 defaultHandler = firstLabelInChain(block.getNode().handler); 414 } 415 } 416 } 417 } 418 419 if (sameDefaultHandler.isEmpty()) { 420 //there is no finally block 421 //it always should be present in default handler 422 return null; 423 } 424 425 TryCatchBlockNodeInfo nextIntervalWithSameDefaultHandler = sameDefaultHandler.get(1); 426 AbstractInsnNode startFinallyChain = tryCatchBlock.getNode().end; 427 AbstractInsnNode endFinallyChainExclusive = skipLastGotoIfNeeded(nextIntervalWithSameDefaultHandler.getNode().handler, 428 nextIntervalWithSameDefaultHandler.getNode().start); 429 430 return new FinallyBlockInfo(startFinallyChain.getNext(), endFinallyChainExclusive); 431 } 432 433 @NotNull 434 private AbstractInsnNode skipLastGotoIfNeeded( 435 @NotNull LabelNode defaultHandlerStartLabel, 436 @NotNull AbstractInsnNode lastFinallyInsExclusive 437 ) { 438 439 AbstractInsnNode prevLast = getPrevNoLineNumberOrLabel(lastFinallyInsExclusive, true); 440 assert prevLast != null : "Empty finally block: " + lastFinallyInsExclusive; 441 442 if (prevLast.getOpcode() == Opcodes.GOTO) { 443 //There we should understand whether goto is jump over catches or last break/continue command inside finally. 444 //If it's a jump over catches so next is true: 445 // 1. jump label should go after default catch handler start label 446 // AND 447 // 2. it shouldn't be present in default catch block, otherwise it break/continue 448 LabelNode targetJump = ((JumpInsnNode) prevLast).label; 449 450 InsnList instructions = inlineFun.instructions; 451 if (instructions.indexOf(defaultHandlerStartLabel) < instructions.indexOf(targetJump)) { //1 condition 452 AbstractInsnNode cur = defaultHandlerStartLabel; 453 while (cur != targetJump) { 454 if (cur.getOpcode() == Opcodes.GOTO) { 455 //noinspection ConstantConditions 456 if (((JumpInsnNode) cur).label == targetJump) { //fail of 2 condition 457 return lastFinallyInsExclusive; 458 } 459 } 460 cur = cur.getNext(); 461 } 462 463 return prevLast; 464 } 465 } 466 return lastFinallyInsExclusive; 467 } 468 469 @NotNull 470 private List<TryCatchBlockNodePosition> findTryCatchBlocksInlinedInFinally(@NotNull FinallyBlockInfo finallyInfo) { 471 List<TryCatchBlockNodePosition> result = new ArrayList<TryCatchBlockNodePosition>(); 472 Map<TryCatchBlockNodeInfo, TryCatchBlockNodePosition> processedBlocks = new HashMap<TryCatchBlockNodeInfo, TryCatchBlockNodePosition>(); 473 for (AbstractInsnNode curInstr = finallyInfo.startIns; curInstr != finallyInfo.endInsExclusive; curInstr = curInstr.getNext()) { 474 if (!(curInstr instanceof LabelNode)) continue; 475 476 LabelNode curLabel = (LabelNode) curInstr; 477 List<TryCatchBlockNodeInfo> startedTryBlocks = tryBlockStarts.get(curLabel); 478 if (startedTryBlocks != null) { 479 for (TryCatchBlockNodeInfo block : startedTryBlocks) { 480 assert !processedBlocks.containsKey(block) : "Try catch block already processed before start label!!! " + block; 481 TryCatchBlockNodePosition info = new TryCatchBlockNodePosition(block, TryCatchPosition.START); 482 processedBlocks.put(block, info); 483 result.add(info); 484 } 485 } 486 487 List<TryCatchBlockNodeInfo> endedTryBlocks = tryBlockEnds.get(curLabel); 488 if (endedTryBlocks == null) continue; 489 490 for (TryCatchBlockNodeInfo block : endedTryBlocks) { 491 TryCatchBlockNodePosition info = processedBlocks.get(block); 492 if (info != null) { 493 assert info.getPosition() == TryCatchPosition.START; 494 info.setPosition(TryCatchPosition.INNER); 495 } 496 else { 497 info = new TryCatchBlockNodePosition(block, TryCatchPosition.END); 498 processedBlocks.put(block, info); 499 result.add(info); 500 } 501 } 502 } 503 return result; 504 } 505 506 private static void substituteReturnValueInFinally( 507 int nonLocalVarIndex, 508 @NotNull Type nonLocalReturnType, 509 @NotNull MethodNode finallyBlockCopy, 510 @NotNull Type localReturnType, 511 boolean doPop 512 ) { 513 if (doPop && localReturnType != Type.VOID_TYPE) { 514 AsmUtil.pop(finallyBlockCopy, localReturnType); 515 } 516 if (nonLocalReturnType != Type.VOID_TYPE) { 517 finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nonLocalVarIndex); 518 } 519 } 520 521 @Nullable 522 private static AbstractInsnNode getPrevNoLineNumberOrLabel(@NotNull AbstractInsnNode node, boolean strict) { 523 AbstractInsnNode result = strict ? node.getPrevious() : node; 524 while (isLineNumberOrLabel(result)) { 525 result = result.getPrevious(); 526 } 527 return result; 528 } 529 530 private void sortTryCatchBlocks(@NotNull List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) { 531 Comparator<TryCatchBlockNodeInfo> comp = new Comparator<TryCatchBlockNodeInfo>() { 532 @Override 533 public int compare(@NotNull TryCatchBlockNodeInfo t1, @NotNull TryCatchBlockNodeInfo t2) { 534 int result = inlineFun.instructions.indexOf(t1.getNode().handler) - inlineFun.instructions.indexOf(t2.getNode().handler); 535 if (result == 0) { 536 result = inlineFun.instructions.indexOf(t1.getNode().start) - inlineFun.instructions.indexOf(t2.getNode().start); 537 if (result == 0) { 538 assert false : "Error: support multicatch finallies!"; 539 result = inlineFun.instructions.indexOf(t1.getNode().end) - inlineFun.instructions.indexOf(t2.getNode().end); 540 } 541 } 542 return result; 543 } 544 }; 545 Collections.sort(inlineFunTryBlockInfo, comp); 546 } 547 }