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