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