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.CommonPackage;
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 = InlinePackage.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 = InlinePackage.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 && !CommonPackage.getIsMeaningful(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 }