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 }