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