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 }