1 // Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.jetbrains.java.decompiler.modules.decompiler;
4 import org.jetbrains.java.decompiler.code.CodeConstants;
5 import org.jetbrains.java.decompiler.code.Instruction;
6 import org.jetbrains.java.decompiler.code.InstructionSequence;
7 import org.jetbrains.java.decompiler.code.SimpleInstructionSequence;
8 import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
9 import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
10 import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
11 import org.jetbrains.java.decompiler.main.DecompilerContext;
12 import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
13 import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
14 import org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;
15 import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;
16 import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
17 import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
18 import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
19 import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
20 import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;
21 import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;
22 import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;
23 import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
24 import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;
25 import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
26 import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
27 import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
28 import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
29 import org.jetbrains.java.decompiler.struct.StructClass;
30 import org.jetbrains.java.decompiler.struct.StructMethod;
31 import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
32 import org.jetbrains.java.decompiler.struct.gen.VarType;
33 import org.jetbrains.java.decompiler.util.InterpreterUtil;
36 import java.util.Map.Entry;
38 public class FinallyProcessor {
39 private final Map<Integer, Integer> finallyBlockIDs = new HashMap<>();
40 private final Map<Integer, Integer> catchallBlockIDs = new HashMap<>();
42 private final MethodDescriptor methodDescriptor;
43 private final VarProcessor varProcessor;
45 public FinallyProcessor(MethodDescriptor md, VarProcessor varProc) {
46 methodDescriptor = md;
47 varProcessor = varProc;
50 public boolean iterateGraph(StructClass cl, StructMethod mt, RootStatement root, ControlFlowGraph graph) {
51 int bytecodeVersion = mt.getBytecodeVersion();
53 LinkedList<Statement> stack = new LinkedList<>();
56 while (!stack.isEmpty()) {
57 Statement stat = stack.removeLast();
59 Statement parent = stat.getParent();
60 if (parent != null && parent.type == Statement.TYPE_CATCH_ALL &&
61 stat == parent.getFirst() && !parent.isCopied()) {
63 CatchAllStatement fin = (CatchAllStatement)parent;
64 BasicBlock head = fin.getBasichead().getBlock();
65 BasicBlock handler = fin.getHandler().getBasichead().getBlock();
67 if (catchallBlockIDs.containsKey(handler.id)) {
70 else if (finallyBlockIDs.containsKey(handler.id)) {
73 Integer var = finallyBlockIDs.get(handler.id);
74 fin.setMonitor(var == null ? null : new VarExprent(var, VarType.VARTYPE_INT, varProcessor));
77 Record inf = getFinallyInformation(cl, mt, root, fin);
79 if (inf == null) { // inconsistent finally
80 catchallBlockIDs.put(handler.id, null);
83 if (DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) {
84 finallyBlockIDs.put(handler.id, null);
87 int varIndex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);
88 insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varIndex, inf, bytecodeVersion);
90 finallyBlockIDs.put(handler.id, varIndex);
93 DeadCodeHelper.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally
94 DeadCodeHelper.removeEmptyBlocks(graph);
95 DeadCodeHelper.mergeBasicBlocks(graph);
102 stack.addAll(stat.getStats());
108 private static final class Record {
109 private final int firstCode;
110 private final Map<BasicBlock, Boolean> mapLast;
112 private Record(int firstCode, Map<BasicBlock, Boolean> mapLast) {
113 this.firstCode = firstCode;
114 this.mapLast = mapLast;
118 private Record getFinallyInformation(StructClass cl, StructMethod mt, RootStatement root, CatchAllStatement fstat) {
119 Map<BasicBlock, Boolean> mapLast = new HashMap<>();
121 BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead();
122 BasicBlock firstBasicBlock = firstBlockStatement.getBlock();
123 Instruction instrFirst = firstBasicBlock.getInstruction(0);
127 switch (instrFirst.opcode) {
128 case CodeConstants.opc_pop:
131 case CodeConstants.opc_astore:
135 ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor);
136 proc.processStatement(root, cl);
138 SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();
139 ssa.splitVariables(root, mt);
141 List<Exprent> lstExprents = firstBlockStatement.getExprents();
143 VarVersionPair varpaar = new VarVersionPair((VarExprent)((AssignmentExprent)lstExprents.get(firstcode == 2 ? 1 : 0)).getLeft());
145 FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();
146 DirectGraph dgraph = flatthelper.buildDirectGraph(root);
148 LinkedList<DirectNode> stack = new LinkedList<>();
149 stack.add(dgraph.first);
151 Set<DirectNode> setVisited = new HashSet<>();
153 while (!stack.isEmpty()) {
154 DirectNode node = stack.removeFirst();
156 if (setVisited.contains(node)) {
159 setVisited.add(node);
161 BasicBlockStatement blockStatement = null;
162 if (node.block != null) {
163 blockStatement = node.block;
165 else if (node.predecessors.size() == 1) {
166 blockStatement = node.predecessors.get(0).block;
169 boolean isTrueExit = true;
171 if (firstcode != 1) {
175 for (int i = 0; i < node.exprents.size(); i++) {
176 Exprent exprent = node.exprents.get(i);
178 if (firstcode == 0) {
179 List<Exprent> lst = exprent.getAllExprents();
182 boolean found = false;
183 for (Exprent expr : lst) {
184 if (expr.type == Exprent.EXPRENT_VAR && new VarVersionPair((VarExprent)expr).equals(varpaar)) {
192 if (exprent.type == Exprent.EXPRENT_EXIT) {
193 ExitExprent exexpr = (ExitExprent)exprent;
194 if (exexpr.getExitType() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR) {
207 else if (firstcode == 2) {
208 // search for a load instruction
209 if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
210 AssignmentExprent assexpr = (AssignmentExprent)exprent;
211 if (assexpr.getRight().type == Exprent.EXPRENT_VAR &&
212 new VarVersionPair((VarExprent)assexpr.getRight()).equals(varpaar)) {
215 if (i == node.exprents.size() - 1) {
216 if (node.successors.size() == 1) {
217 DirectNode nd = node.successors.get(0);
218 if (!nd.exprents.isEmpty()) {
219 next = nd.exprents.get(0);
224 next = node.exprents.get(i + 1);
227 boolean found = false;
228 if (next != null && next.type == Exprent.EXPRENT_EXIT) {
229 ExitExprent exexpr = (ExitExprent)next;
230 if (exexpr.getExitType() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_VAR
231 && assexpr.getLeft().equals(exexpr.getValue())) {
248 // find finally exits
249 if (blockStatement != null && blockStatement.getBlock() != null) {
250 Statement handler = fstat.getHandler();
251 for (StatEdge edge : blockStatement.getSuccessorEdges(Statement.STATEDGE_DIRECT_ALL)) {
252 if (edge.getType() != StatEdge.TYPE_REGULAR && handler.containsStatement(blockStatement)
253 && !handler.containsStatement(edge.getDestination())) {
254 Boolean existingFlag = mapLast.get(blockStatement.getBlock());
255 // note: the dummy node is also processed!
256 if (existingFlag == null || !existingFlag) {
257 mapLast.put(blockStatement.getBlock(), isTrueExit);
264 stack.addAll(node.successors);
267 // empty finally block?
268 if (fstat.getHandler().type == Statement.TYPE_BASIC_BLOCK) {
270 boolean isEmpty = false;
271 boolean isFirstLast = mapLast.containsKey(firstBasicBlock);
272 InstructionSequence seq = firstBasicBlock.getSeq();
276 isEmpty = isFirstLast && seq.length() == 1;
279 isEmpty = seq.length() == 1;
282 isEmpty = isFirstLast ? seq.length() == 3 : seq.length() == 1;
290 return new Record(firstcode, mapLast);
293 private static void insertSemaphore(ControlFlowGraph graph,
294 Set<BasicBlock> setTry,
299 int bytecode_version) {
300 Set<BasicBlock> setCopy = new HashSet<>(setTry);
302 int finallytype = information.firstCode;
303 Map<BasicBlock, Boolean> mapLast = information.mapLast;
305 // first and last statements
306 removeExceptionInstructionsEx(handler, 1, finallytype);
307 for (Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) {
308 BasicBlock last = entry.getKey();
310 if (entry.getValue()) {
311 removeExceptionInstructionsEx(last, 2, finallytype);
312 graph.getFinallyExits().add(last);
316 // disable semaphore at statement exit points
317 for (BasicBlock block : setTry) {
318 List<BasicBlock> lstSucc = block.getSuccessors();
320 for (BasicBlock dest : lstSucc) {
322 if (dest != graph.getLast() && !setCopy.contains(dest)) {
324 SimpleInstructionSequence seq = new SimpleInstructionSequence();
325 seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1);
326 seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1);
328 // build a separate block
329 BasicBlock newBlock = new BasicBlock(++graph.last_id, seq);
331 // insert between block and dest
332 block.replaceSuccessor(dest, newBlock);
333 newBlock.addSuccessor(dest);
334 setCopy.add(newBlock);
335 graph.getBlocks().addWithKey(newBlock, newBlock.id);
338 // FIXME: special case synchronized
340 // copy exception edges and extend protected ranges
341 for (int j = 0; j < block.getSuccessorExceptions().size(); j++) {
342 BasicBlock hd = block.getSuccessorExceptions().get(j);
343 newBlock.addSuccessorException(hd);
345 ExceptionRangeCFG range = graph.getExceptionRange(hd, block);
346 range.getProtectedRange().add(newBlock);
352 // enable semaphore at the statement entrance
353 SimpleInstructionSequence seq = new SimpleInstructionSequence();
354 seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{1}), -1);
355 seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1);
357 BasicBlock newHead = new BasicBlock(++graph.last_id, seq);
359 insertBlockBefore(graph, head, newHead);
361 // initialize semaphore with false
362 seq = new SimpleInstructionSequence();
363 seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1);
364 seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{var}), -1);
366 BasicBlock newHeadInit = new BasicBlock(++graph.last_id, seq);
368 insertBlockBefore(graph, newHead, newHeadInit);
370 setCopy.add(newHead);
371 setCopy.add(newHeadInit);
373 for (BasicBlock hd : new HashSet<>(newHeadInit.getSuccessorExceptions())) {
374 ExceptionRangeCFG range = graph.getExceptionRange(hd, newHeadInit);
376 if (setCopy.containsAll(range.getProtectedRange())) {
377 newHeadInit.removeSuccessorException(hd);
378 range.getProtectedRange().remove(newHeadInit);
383 private static void insertBlockBefore(ControlFlowGraph graph, BasicBlock oldblock, BasicBlock newblock) {
384 List<BasicBlock> lstTemp = new ArrayList<>();
385 lstTemp.addAll(oldblock.getPredecessors());
386 lstTemp.addAll(oldblock.getPredecessorExceptions());
388 // replace predecessors
389 for (BasicBlock pred : lstTemp) {
390 pred.replaceSuccessor(oldblock, newblock);
393 // copy exception edges and extend protected ranges
394 for (BasicBlock hd : oldblock.getSuccessorExceptions()) {
395 newblock.addSuccessorException(hd);
397 ExceptionRangeCFG range = graph.getExceptionRange(hd, oldblock);
398 range.getProtectedRange().add(newblock);
402 for (ExceptionRangeCFG range : graph.getExceptions()) {
403 if (range.getHandler() == oldblock) {
404 range.setHandler(newblock);
408 newblock.addSuccessor(oldblock);
409 graph.getBlocks().addWithKey(newblock, newblock.id);
410 if (graph.getFirst() == oldblock) {
411 graph.setFirst(newblock);
415 private static Set<BasicBlock> getAllBasicBlocks(Statement stat) {
416 List<Statement> lst = new LinkedList<>();
421 Statement st = lst.get(index);
423 if (st.type == Statement.TYPE_BASIC_BLOCK) {
427 lst.addAll(st.getStats());
431 while (index < lst.size());
433 Set<BasicBlock> res = new HashSet<>();
435 for (Statement st : lst) {
436 res.add(((BasicBlockStatement)st).getBlock());
442 private boolean verifyFinallyEx(ControlFlowGraph graph, CatchAllStatement fstat, Record information) {
443 Set<BasicBlock> tryBlocks = getAllBasicBlocks(fstat.getFirst());
444 Set<BasicBlock> catchBlocks = getAllBasicBlocks(fstat.getHandler());
446 int finallytype = information.firstCode;
447 Map<BasicBlock, Boolean> mapLast = information.mapLast;
449 BasicBlock first = fstat.getHandler().getBasichead().getBlock();
450 boolean skippedFirst = false;
452 if (finallytype == 3) {
454 removeExceptionInstructionsEx(first, 3, finallytype);
456 if (mapLast.containsKey(first)) {
457 graph.getFinallyExits().add(first);
463 if (first.getSeq().length() == 1 && finallytype > 0) {
464 BasicBlock firstsuc = first.getSuccessors().get(0);
465 if (catchBlocks.contains(firstsuc)) {
472 // identify start blocks
473 Set<BasicBlock> startBlocks = new HashSet<>();
474 for (BasicBlock block : tryBlocks) {
475 startBlocks.addAll(block.getSuccessors());
477 // throw in the try body will point directly to the dummy exit
478 // so remove dummy exit
479 startBlocks.remove(graph.getLast());
480 startBlocks.removeAll(tryBlocks);
482 List<Area> lstAreas = new ArrayList<>();
484 for (BasicBlock start : startBlocks) {
486 Area arr = compareSubgraphsEx(graph, start, catchBlocks, first, finallytype, mapLast, skippedFirst);
495 // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
496 // } catch(Exception ex){ex.printStackTrace();}
499 for (Area area : lstAreas) {
500 deleteArea(graph, area);
504 // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
505 // } catch(Exception ex){ex.printStackTrace();}
507 // INFO: empty basic blocks may remain in the graph!
508 for (Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) {
509 BasicBlock last = entry.getKey();
511 if (entry.getValue()) {
512 removeExceptionInstructionsEx(last, 2, finallytype);
513 graph.getFinallyExits().add(last);
517 removeExceptionInstructionsEx(fstat.getHandler().getBasichead().getBlock(), 1, finallytype);
522 private static final class Area {
523 private final BasicBlock start;
524 private final Set<BasicBlock> sample;
525 private final BasicBlock next;
527 private Area(BasicBlock start, Set<BasicBlock> sample, BasicBlock next) {
529 this.sample = sample;
534 private Area compareSubgraphsEx(ControlFlowGraph graph,
535 BasicBlock startSample,
536 Set<BasicBlock> catchBlocks,
537 BasicBlock startCatch,
539 Map<BasicBlock, Boolean> mapLast,
540 boolean skippedFirst) {
541 class BlockStackEntry {
542 public final BasicBlock blockCatch;
543 public final BasicBlock blockSample;
545 // TODO: correct handling (merging) of multiple paths
546 public final List<int[]> lstStoreVars;
548 BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List<int[]> lstStoreVars) {
549 this.blockCatch = blockCatch;
550 this.blockSample = blockSample;
551 this.lstStoreVars = new ArrayList<>(lstStoreVars);
555 List<BlockStackEntry> stack = new LinkedList<>();
557 Set<BasicBlock> setSample = new HashSet<>();
559 Map<String, BasicBlock[]> mapNext = new HashMap<>();
561 stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<>()));
563 while (!stack.isEmpty()) {
565 BlockStackEntry entry = stack.remove(0);
566 BasicBlock blockCatch = entry.blockCatch;
567 BasicBlock blockSample = entry.blockSample;
569 boolean isFirstBlock = !skippedFirst && blockCatch == startCatch;
570 boolean isLastBlock = mapLast.containsKey(blockCatch);
571 boolean isTrueLastBlock = isLastBlock && mapLast.get(blockCatch);
573 if (!compareBasicBlocksEx(graph, blockCatch, blockSample, (isFirstBlock ? 1 : 0) | (isTrueLastBlock ? 2 : 0), finallytype,
574 entry.lstStoreVars)) {
578 if (blockSample.getSuccessors().size() != blockCatch.getSuccessors().size()) {
582 setSample.add(blockSample);
585 for (int i = 0; i < blockCatch.getSuccessors().size(); i++) {
586 BasicBlock sucCatch = blockCatch.getSuccessors().get(i);
587 BasicBlock sucSample = blockSample.getSuccessors().get(i);
589 if (catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) {
590 stack.add(new BlockStackEntry(sucCatch, sucSample, entry.lstStoreVars));
594 // exception successors
595 if (isLastBlock && blockSample.getSeq().isEmpty()) {
596 // do nothing, blockSample will be removed anyway
599 if (blockCatch.getSuccessorExceptions().size() == blockSample.getSuccessorExceptions().size()) {
600 for (int i = 0; i < blockCatch.getSuccessorExceptions().size(); i++) {
601 BasicBlock sucCatch = blockCatch.getSuccessorExceptions().get(i);
602 BasicBlock sucSample = blockSample.getSuccessorExceptions().get(i);
604 String excCatch = graph.getExceptionRange(sucCatch, blockCatch).getUniqueExceptionsString();
605 String excSample = graph.getExceptionRange(sucSample, blockSample).getUniqueExceptionsString();
607 // FIXME: compare handlers if possible
608 boolean equalexc = excCatch == null ? excSample == null : excCatch.equals(excSample);
611 if (catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) {
613 List<int[]> lst = entry.lstStoreVars;
615 if (sucCatch.getSeq().length() > 0 && sucSample.getSeq().length() > 0) {
616 Instruction instrCatch = sucCatch.getSeq().getInstr(0);
617 Instruction instrSample = sucSample.getSeq().getInstr(0);
619 if (instrCatch.opcode == CodeConstants.opc_astore &&
620 instrSample.opcode == CodeConstants.opc_astore) {
621 lst = new ArrayList<>(lst);
622 lst.add(new int[]{instrCatch.operand(0), instrSample.operand(0)});
626 stack.add(new BlockStackEntry(sucCatch, sucSample, lst));
640 Set<BasicBlock> setSuccs = new HashSet<>(blockSample.getSuccessors());
641 setSuccs.removeAll(setSample);
643 for (BlockStackEntry stackent : stack) {
644 setSuccs.remove(stackent.blockSample);
647 for (BasicBlock succ : setSuccs) {
648 if (graph.getLast() != succ) { // FIXME: why?
649 mapNext.put(blockSample.id + "#" + succ.id, new BasicBlock[]{blockSample, succ, isTrueLastBlock ? succ : null});
655 return new Area(startSample, setSample, getUniqueNext(graph, new HashSet<>(mapNext.values())));
658 private static BasicBlock getUniqueNext(ControlFlowGraph graph, Set<BasicBlock[]> setNext) {
659 // precondition: there is at most one true exit path in a finally statement
661 BasicBlock next = null;
662 boolean multiple = false;
664 for (BasicBlock[] arr : setNext) {
666 if (arr[2] != null) {
675 else if (next != arr[1]) {
679 if (arr[1].getPredecessors().size() == 1) {
685 if (multiple) { // TODO: generic solution
686 for (BasicBlock[] arr : setNext) {
687 BasicBlock block = arr[1];
690 if (InterpreterUtil.equalSets(next.getSuccessors(), block.getSuccessors())) {
691 InstructionSequence seqNext = next.getSeq();
692 InstructionSequence seqBlock = block.getSeq();
694 if (seqNext.length() == seqBlock.length()) {
695 for (int i = 0; i < seqNext.length(); i++) {
696 Instruction instrNext = seqNext.getInstr(i);
697 Instruction instrBlock = seqBlock.getInstr(i);
699 if (!Instruction.equals(instrNext, instrBlock)) {
702 for (int j = 0; j < instrNext.operandsCount(); j++) {
703 if (instrNext.operand(j) != instrBlock.operand(j)) {
720 // DotExporter.toDotFile(graph, new File("c:\\Temp\\fern5.dot"), true);
721 // } catch(IOException ex) {
722 // ex.printStackTrace();
725 for (BasicBlock[] arr : setNext) {
726 if (arr[1] != next) {
727 // FIXME: exception edge possible?
728 arr[0].removeSuccessor(arr[1]);
729 arr[0].addSuccessor(next);
733 DeadCodeHelper.removeDeadBlocks(graph);
739 private boolean compareBasicBlocksEx(ControlFlowGraph graph,
744 List<int[]> lstStoreVars) {
745 InstructionSequence seqPattern = pattern.getSeq();
746 InstructionSequence seqSample = sample.getSeq();
749 seqPattern = seqPattern.clone();
751 if ((type & 1) > 0) { // first
752 if (finallytype > 0) {
753 seqPattern.removeInstruction(0);
757 if ((type & 2) > 0) { // last
758 if (finallytype == 0 || finallytype == 2) {
759 seqPattern.removeLast();
762 if (finallytype == 2) {
763 seqPattern.removeLast();
768 if (seqPattern.length() > seqSample.length()) {
772 for (int i = 0; i < seqPattern.length(); i++) {
773 Instruction instrPattern = seqPattern.getInstr(i);
774 Instruction instrSample = seqSample.getInstr(i);
776 // compare instructions with respect to jumps
777 if (!equalInstructions(instrPattern, instrSample, lstStoreVars)) {
782 if (seqPattern.length() < seqSample.length()) { // split in two blocks
783 SimpleInstructionSequence seq = new SimpleInstructionSequence();
784 LinkedList<Integer> oldOffsets = new LinkedList<>();
785 for (int i = seqSample.length() - 1; i >= seqPattern.length(); i--) {
786 seq.addInstruction(0, seqSample.getInstr(i), -1);
787 oldOffsets.addFirst(sample.getOriginalOffset(i));
788 seqSample.removeInstruction(i);
791 BasicBlock newBlock = new BasicBlock(++graph.last_id, seq);
792 newBlock.getOriginalOffsets().addAll(oldOffsets);
794 List<BasicBlock> lstTemp = new ArrayList<>(sample.getSuccessors());
797 for (BasicBlock suc : lstTemp) {
798 sample.removeSuccessor(suc);
799 newBlock.addSuccessor(suc);
802 sample.addSuccessor(newBlock);
804 graph.getBlocks().addWithKey(newBlock, newBlock.id);
806 Set<BasicBlock> setFinallyExits = graph.getFinallyExits();
807 if (setFinallyExits.contains(sample)) {
808 setFinallyExits.remove(sample);
809 setFinallyExits.add(newBlock);
812 // copy exception edges and extend protected ranges
813 for (int j = 0; j < sample.getSuccessorExceptions().size(); j++) {
814 BasicBlock hd = sample.getSuccessorExceptions().get(j);
815 newBlock.addSuccessorException(hd);
817 ExceptionRangeCFG range = graph.getExceptionRange(hd, sample);
818 range.getProtectedRange().add(newBlock);
825 public boolean equalInstructions(Instruction first, Instruction second, List<int[]> lstStoreVars) {
826 if (!Instruction.equals(first, second)) {
830 if (first.group != CodeConstants.GROUP_JUMP) { // FIXME: switch comparison
831 for (int i = 0; i < first.operandsCount(); i++) {
832 int firstOp = first.operand(i);
833 int secondOp = second.operand(i);
834 if (firstOp != secondOp) {
835 // a-load/store instructions
836 if (first.opcode == CodeConstants.opc_aload || first.opcode == CodeConstants.opc_astore) {
837 for (int[] arr : lstStoreVars) {
838 if (arr[0] == firstOp && arr[1] == secondOp) {
852 private static void deleteArea(ControlFlowGraph graph, Area area) {
853 BasicBlock start = area.start;
854 BasicBlock next = area.next;
862 next = graph.getLast();
865 // collect common exception ranges of predecessors and successors
866 Set<BasicBlock> setCommonExceptionHandlers = new HashSet<>(next.getSuccessorExceptions());
867 for (BasicBlock pred : start.getPredecessors()) {
868 setCommonExceptionHandlers.retainAll(pred.getSuccessorExceptions());
871 boolean is_outside_range = false;
873 Set<BasicBlock> setPredecessors = new HashSet<>(start.getPredecessors());
875 // replace start with next
876 for (BasicBlock pred : setPredecessors) {
877 pred.replaceSuccessor(start, next);
880 Set<BasicBlock> setBlocks = area.sample;
882 Set<ExceptionRangeCFG> setCommonRemovedExceptionRanges = null;
884 // remove all the blocks inbetween
885 for (BasicBlock block : setBlocks) {
887 // artificial basic blocks (those resulted from splitting)
888 // can belong to more than one area
889 if (graph.getBlocks().containsKey(block.id)) {
891 if (!block.getSuccessorExceptions().containsAll(setCommonExceptionHandlers)) {
892 is_outside_range = true;
895 Set<ExceptionRangeCFG> setRemovedExceptionRanges = new HashSet<>();
896 for (BasicBlock handler : block.getSuccessorExceptions()) {
897 setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block));
900 if (setCommonRemovedExceptionRanges == null) {
901 setCommonRemovedExceptionRanges = setRemovedExceptionRanges;
904 setCommonRemovedExceptionRanges.retainAll(setRemovedExceptionRanges);
907 // shift extern edges on splitted blocks
908 if (block.getSeq().isEmpty() && block.getSuccessors().size() == 1) {
909 BasicBlock succs = block.getSuccessors().get(0);
910 for (BasicBlock pred : new ArrayList<>(block.getPredecessors())) {
911 if (!setBlocks.contains(pred)) {
912 pred.replaceSuccessor(block, succs);
916 if (graph.getFirst() == block) {
917 graph.setFirst(succs);
921 graph.removeBlock(block);
925 if (is_outside_range) {
927 BasicBlock emptyblock = new BasicBlock(++graph.last_id);
929 graph.getBlocks().addWithKey(emptyblock, emptyblock.id);
931 // add to ranges if necessary
932 for (ExceptionRangeCFG range : setCommonRemovedExceptionRanges) {
933 emptyblock.addSuccessorException(range.getHandler());
934 range.getProtectedRange().add(emptyblock);
937 // insert between predecessors and next
938 emptyblock.addSuccessor(next);
939 for (BasicBlock pred : setPredecessors) {
940 pred.replaceSuccessor(next, emptyblock);
945 private static void removeExceptionInstructionsEx(BasicBlock block, int blocktype, int finallytype) {
946 InstructionSequence seq = block.getSeq();
948 if (finallytype == 3) { // empty finally handler
949 for (int i = seq.length() - 1; i >= 0; i--) {
950 seq.removeInstruction(i);
954 if ((blocktype & 1) > 0) { // first
955 if (finallytype == 2 || finallytype == 1) { // astore or pop
956 seq.removeInstruction(0);
960 if ((blocktype & 2) > 0) { // last
961 if (finallytype == 2 || finallytype == 0) {
965 if (finallytype == 2) { // astore