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    }