f29a45008525be7e84a4ca4cf89c910bc2dc2821
[idea/community.git] / plugins / java-decompiler / engine / src / org / jetbrains / java / decompiler / modules / decompiler / stats / Statement.java
1 /*
2  * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
3  */
4 package org.jetbrains.java.decompiler.modules.decompiler.stats;
5
6 import org.jetbrains.java.decompiler.code.CodeConstants;
7 import org.jetbrains.java.decompiler.code.InstructionSequence;
8 import org.jetbrains.java.decompiler.main.DecompilerContext;
9 import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;
10 import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
11 import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
12 import org.jetbrains.java.decompiler.modules.decompiler.StrongConnectivityHelper;
13 import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
14 import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement.LoopType;
15 import org.jetbrains.java.decompiler.struct.match.IMatchable;
16 import org.jetbrains.java.decompiler.struct.match.MatchEngine;
17 import org.jetbrains.java.decompiler.struct.match.MatchNode;
18 import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;
19 import org.jetbrains.java.decompiler.util.TextBuffer;
20 import org.jetbrains.java.decompiler.util.VBStyleCollection;
21
22 import java.util.*;
23 import java.util.Map.Entry;
24
25 public class Statement implements IMatchable {
26   public static final int STATEDGE_ALL = 0x80000000;
27   public static final int STATEDGE_DIRECT_ALL = 0x40000000;
28
29   public static final int DIRECTION_BACKWARD = 0;
30   public static final int DIRECTION_FORWARD = 1;
31
32   public static final int TYPE_GENERAL = 0;
33   public static final int TYPE_IF = 2;
34   public static final int TYPE_DO = 5;
35   public static final int TYPE_SWITCH = 6;
36   public static final int TYPE_TRY_CATCH = 7;
37   public static final int TYPE_BASIC_BLOCK = 8;
38   //public static final int TYPE_FINALLY = 9;
39   public static final int TYPE_SYNCHRONIZED = 10;
40   public static final int TYPE_PLACEHOLDER = 11;
41   public static final int TYPE_CATCH_ALL = 12;
42   public static final int TYPE_ROOT = 13;
43   public static final int TYPE_DUMMY_EXIT = 14;
44   public static final int TYPE_SEQUENCE = 15;
45
46   public static final int LASTBASICTYPE_IF = 0;
47   public static final int LASTBASICTYPE_SWITCH = 1;
48   public static final int LASTBASICTYPE_GENERAL = 2;
49
50
51   // *****************************************************************************
52   // public fields
53   // *****************************************************************************
54
55   public int type;
56
57   public Integer id;
58
59   // *****************************************************************************
60   // private fields
61   // *****************************************************************************
62
63   private final Map<Integer, List<StatEdge>> mapSuccEdges = new HashMap<>();
64   private final Map<Integer, List<StatEdge>> mapPredEdges = new HashMap<>();
65
66   private final Map<Integer, List<Statement>> mapSuccStates = new HashMap<>();
67   private final Map<Integer, List<Statement>> mapPredStates = new HashMap<>();
68
69   // statement as graph
70   protected final VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<>();
71
72   protected Statement parent;
73
74   protected Statement first;
75
76   protected List<Exprent> exprents;
77
78   protected final HashSet<StatEdge> labelEdges = new HashSet<>();
79
80   protected final List<Exprent> varDefinitions = new ArrayList<>();
81
82   // copied statement, s. deobfuscating of irreducible CFGs
83   private boolean copied = false;
84
85   // relevant for the first stage of processing only
86   // set to null after initializing of the statement structure
87
88   protected Statement post;
89
90   protected int lastBasicType = LASTBASICTYPE_GENERAL;
91
92   protected boolean isMonitorEnter;
93
94   protected boolean containsMonitorExit;
95
96   protected HashSet<Statement> continueSet = new HashSet<>();
97
98   // *****************************************************************************
99   // initializers
100   // *****************************************************************************
101
102   {
103     // set statement id
104     id = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER);
105   }
106
107   Statement(int type) {
108     this.type = type;
109   }
110
111   // *****************************************************************************
112   // public methods
113   // *****************************************************************************
114
115   public void clearTempInformation() {
116
117     post = null;
118     continueSet = null;
119
120     copied = false;
121     // FIXME: used in FlattenStatementsHelper.flattenStatement()! check and remove
122     //lastBasicType = LASTBASICTYPE_GENERAL;
123     isMonitorEnter = false;
124     containsMonitorExit = false;
125
126     processMap(mapSuccEdges);
127     processMap(mapPredEdges);
128     processMap(mapSuccStates);
129     processMap(mapPredStates);
130   }
131
132   private static <T> void processMap(Map<Integer, List<T>> map) {
133     map.remove(StatEdge.TYPE_EXCEPTION);
134
135     List<T> lst = map.get(STATEDGE_DIRECT_ALL);
136     if (lst != null) {
137       map.put(STATEDGE_ALL, new ArrayList<>(lst));
138     }
139     else {
140       map.remove(STATEDGE_ALL);
141     }
142   }
143
144   public void collapseNodesToStatement(Statement stat) {
145
146     Statement head = stat.getFirst();
147     Statement post = stat.getPost();
148
149     VBStyleCollection<Statement, Integer> setNodes = stat.getStats();
150
151     // post edges
152     if (post != null) {
153       for (StatEdge edge : post.getEdges(STATEDGE_DIRECT_ALL, DIRECTION_BACKWARD)) {
154         if (stat.containsStatementStrict(edge.getSource())) {
155           edge.getSource().changeEdgeType(DIRECTION_FORWARD, edge, StatEdge.TYPE_BREAK);
156           stat.addLabeledEdge(edge);
157         }
158       }
159     }
160
161     // regular head edges
162     for (StatEdge prededge : head.getAllPredecessorEdges()) {
163
164       if (prededge.getType() != StatEdge.TYPE_EXCEPTION &&
165           stat.containsStatementStrict(prededge.getSource())) {
166         prededge.getSource().changeEdgeType(DIRECTION_FORWARD, prededge, StatEdge.TYPE_CONTINUE);
167         stat.addLabeledEdge(prededge);
168       }
169
170       head.removePredecessor(prededge);
171       prededge.getSource().changeEdgeNode(DIRECTION_FORWARD, prededge, stat);
172       stat.addPredecessor(prededge);
173     }
174
175     if (setNodes.containsKey(first.id)) {
176       first = stat;
177     }
178
179     // exception edges
180     Set<Statement> setHandlers = new HashSet<>(head.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD));
181     for (Statement node : setNodes) {
182       setHandlers.retainAll(node.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD));
183     }
184
185     if (!setHandlers.isEmpty()) {
186
187       for (StatEdge edge : head.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) {
188         Statement handler = edge.getDestination();
189
190         if (setHandlers.contains(handler)) {
191           if (!setNodes.containsKey(handler.id)) {
192             stat.addSuccessor(new StatEdge(stat, handler, edge.getExceptions()));
193           }
194         }
195       }
196
197       for (Statement node : setNodes) {
198         for (StatEdge edge : node.getEdges(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD)) {
199           if (setHandlers.contains(edge.getDestination())) {
200             node.removeSuccessor(edge);
201           }
202         }
203       }
204     }
205
206     if (post != null &&
207         !stat.getNeighbours(StatEdge.TYPE_EXCEPTION, DIRECTION_FORWARD).contains(post)) { // TODO: second condition redundant?
208       stat.addSuccessor(new StatEdge(StatEdge.TYPE_REGULAR, stat, post));
209     }
210
211
212     // adjust statement collection
213     for (Statement st : setNodes) {
214       stats.removeWithKey(st.id);
215     }
216
217     stats.addWithKey(stat, stat.id);
218
219     stat.setAllParent();
220     stat.setParent(this);
221
222     stat.buildContinueSet();
223     // monitorenter and monitorexit
224     stat.buildMonitorFlags();
225
226     if (stat.type == TYPE_SWITCH) {
227       // special case switch, sorting leaf nodes
228       ((SwitchStatement)stat).sortEdgesAndNodes();
229     }
230   }
231
232   public void setAllParent() {
233     for (Statement st : stats) {
234       st.setParent(this);
235     }
236   }
237
238   public void addLabeledEdge(StatEdge edge) {
239
240     if (edge.closure != null) {
241       edge.closure.getLabelEdges().remove(edge);
242     }
243     edge.closure = this;
244     this.getLabelEdges().add(edge);
245   }
246
247   private void addEdgeDirectInternal(int direction, StatEdge edge, int edgetype) {
248     Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
249     Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
250
251     mapEdges.computeIfAbsent(edgetype, k -> new ArrayList<>()).add(edge);
252
253     mapStates.computeIfAbsent(edgetype, k -> new ArrayList<>()).add(direction == DIRECTION_BACKWARD ? edge.getSource() : edge.getDestination());
254   }
255
256   private void addEdgeInternal(int direction, StatEdge edge) {
257     int type = edge.getType();
258
259     int[] arrtypes;
260     if (type == StatEdge.TYPE_EXCEPTION) {
261       arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION};
262     }
263     else {
264       arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type};
265     }
266
267     for (int edgetype : arrtypes) {
268       addEdgeDirectInternal(direction, edge, edgetype);
269     }
270   }
271
272   private void removeEdgeDirectInternal(int direction, StatEdge edge, int edgetype) {
273
274     Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
275     Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
276
277     List<StatEdge> lst = mapEdges.get(edgetype);
278     if (lst != null) {
279       int index = lst.indexOf(edge);
280       if (index >= 0) {
281         lst.remove(index);
282         mapStates.get(edgetype).remove(index);
283       }
284     }
285   }
286
287   private void removeEdgeInternal(int direction, StatEdge edge) {
288
289     int type = edge.getType();
290
291     int[] arrtypes;
292     if (type == StatEdge.TYPE_EXCEPTION) {
293       arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION};
294     }
295     else {
296       arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type};
297     }
298
299     for (int edgetype : arrtypes) {
300       removeEdgeDirectInternal(direction, edge, edgetype);
301     }
302   }
303
304   public void addPredecessor(StatEdge edge) {
305     addEdgeInternal(DIRECTION_BACKWARD, edge);
306   }
307
308   public void removePredecessor(StatEdge edge) {
309
310     if (edge == null) {  // FIXME: redundant?
311       return;
312     }
313
314     removeEdgeInternal(DIRECTION_BACKWARD, edge);
315   }
316
317   public void addSuccessor(StatEdge edge) {
318     addEdgeInternal(DIRECTION_FORWARD, edge);
319
320     if (edge.closure != null) {
321       edge.closure.getLabelEdges().add(edge);
322     }
323
324     edge.getDestination().addPredecessor(edge);
325   }
326
327   public void removeSuccessor(StatEdge edge) {
328
329     if (edge == null) {
330       return;
331     }
332
333     removeEdgeInternal(DIRECTION_FORWARD, edge);
334
335     if (edge.closure != null) {
336       edge.closure.getLabelEdges().remove(edge);
337     }
338
339     if (edge.getDestination() != null) {  // TODO: redundant?
340       edge.getDestination().removePredecessor(edge);
341     }
342   }
343
344   // TODO: make obsolete and remove
345   public void removeAllSuccessors(Statement stat) {
346
347     if (stat == null) {
348       return;
349     }
350
351     for (StatEdge edge : getAllSuccessorEdges()) {
352       if (edge.getDestination() == stat) {
353         removeSuccessor(edge);
354       }
355     }
356   }
357
358   public HashSet<Statement> buildContinueSet() {
359     continueSet.clear();
360
361     for (Statement st : stats) {
362       continueSet.addAll(st.buildContinueSet());
363       if (st != first) {
364         continueSet.remove(st.getBasichead());
365       }
366     }
367
368     for (StatEdge edge : getEdges(StatEdge.TYPE_CONTINUE, DIRECTION_FORWARD)) {
369       continueSet.add(edge.getDestination().getBasichead());
370     }
371
372     if (type == TYPE_DO) {
373       continueSet.remove(first.getBasichead());
374     }
375
376     return continueSet;
377   }
378
379   public void buildMonitorFlags() {
380
381     for (Statement st : stats) {
382       st.buildMonitorFlags();
383     }
384
385     switch (type) {
386       case TYPE_BASIC_BLOCK:
387         BasicBlockStatement bblock = (BasicBlockStatement)this;
388         InstructionSequence seq = bblock.getBlock().getSeq();
389
390         if (seq != null && seq.length() > 0) {
391           for (int i = 0; i < seq.length(); i++) {
392             if (seq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {
393               containsMonitorExit = true;
394               break;
395             }
396           }
397           isMonitorEnter = (seq.getLastInstr().opcode == CodeConstants.opc_monitorenter);
398         }
399         break;
400       case TYPE_SEQUENCE:
401       case TYPE_IF:
402         containsMonitorExit = false;
403         for (Statement st : stats) {
404           containsMonitorExit |= st.isContainsMonitorExit();
405         }
406
407         break;
408       case TYPE_SYNCHRONIZED:
409       case TYPE_ROOT:
410       case TYPE_GENERAL:
411         break;
412       default:
413         containsMonitorExit = false;
414         for (Statement st : stats) {
415           containsMonitorExit |= st.isContainsMonitorExit();
416         }
417     }
418   }
419
420
421   public List<Statement> getReversePostOrderList() {
422     return getReversePostOrderList(first);
423   }
424
425   public List<Statement> getReversePostOrderList(Statement stat) {
426     List<Statement> res = new ArrayList<>();
427
428     addToReversePostOrderListIterative(stat, res);
429
430     return res;
431   }
432
433   public List<Statement> getPostReversePostOrderList() {
434     return getPostReversePostOrderList(null);
435   }
436
437   public List<Statement> getPostReversePostOrderList(List<Statement> lstexits) {
438
439     List<Statement> res = new ArrayList<>();
440
441     if (lstexits == null) {
442       StrongConnectivityHelper schelper = new StrongConnectivityHelper(this);
443       lstexits = StrongConnectivityHelper.getExitReps(schelper.getComponents());
444     }
445
446     HashSet<Statement> setVisited = new HashSet<>();
447
448     for (Statement exit : lstexits) {
449       addToPostReversePostOrderList(exit, res, setVisited);
450     }
451
452     if (res.size() != stats.size()) {
453       throw new RuntimeException("computing post reverse post order failed!");
454     }
455
456     return res;
457   }
458
459   public boolean containsStatement(Statement stat) {
460     return this == stat || containsStatementStrict(stat);
461   }
462
463   public boolean containsStatementStrict(Statement stat) {
464     if (stats.contains(stat)) {
465       return true;
466     }
467
468     for (Statement st : stats) {
469       if (st.containsStatementStrict(stat)) {
470         return true;
471       }
472     }
473
474     return false;
475   }
476
477   public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {
478     throw new RuntimeException("not implemented");
479   }
480
481   // TODO: make obsolete and remove
482   public List<Object> getSequentialObjects() {
483     return new ArrayList<>(stats);
484   }
485
486   public void initExprents() {
487     // do nothing
488   }
489
490   public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
491     // do nothing
492   }
493
494   public Statement getSimpleCopy() {
495     throw new RuntimeException("not implemented");
496   }
497
498   public void initSimpleCopy() {
499     if (!stats.isEmpty()) {
500       first = stats.get(0);
501     }
502   }
503
504   public void replaceStatement(Statement oldstat, Statement newstat) {
505
506     for (StatEdge edge : oldstat.getAllPredecessorEdges()) {
507       oldstat.removePredecessor(edge);
508       edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, newstat);
509       newstat.addPredecessor(edge);
510     }
511
512     for (StatEdge edge : oldstat.getAllSuccessorEdges()) {
513       oldstat.removeSuccessor(edge);
514       edge.setSource(newstat);
515       newstat.addSuccessor(edge);
516     }
517
518     int statindex = stats.getIndexByKey(oldstat.id);
519     stats.removeWithKey(oldstat.id);
520     stats.addWithKeyAndIndex(statindex, newstat, newstat.id);
521
522     newstat.setParent(this);
523     newstat.post = oldstat.post;
524
525     if (first == oldstat) {
526       first = newstat;
527     }
528
529     List<StatEdge> lst = new ArrayList<>(oldstat.getLabelEdges());
530
531     for (int i = lst.size() - 1; i >= 0; i--) {
532       StatEdge edge = lst.get(i);
533       if (edge.getSource() != newstat) {
534         newstat.addLabeledEdge(edge);
535       }
536       else {
537         if (this == edge.getDestination() || this.containsStatementStrict(edge.getDestination())) {
538           edge.closure = null;
539         }
540         else {
541           this.addLabeledEdge(edge);
542         }
543       }
544     }
545
546     oldstat.getLabelEdges().clear();
547   }
548
549
550   // *****************************************************************************
551   // private methods
552   // *****************************************************************************
553
554   private static void addToReversePostOrderListIterative(Statement root, List<? super Statement> lst) {
555
556     LinkedList<Statement> stackNode = new LinkedList<>();
557     LinkedList<Integer> stackIndex = new LinkedList<>();
558     HashSet<Statement> setVisited = new HashSet<>();
559
560     stackNode.add(root);
561     stackIndex.add(0);
562
563     while (!stackNode.isEmpty()) {
564
565       Statement node = stackNode.getLast();
566       int index = stackIndex.removeLast();
567
568       setVisited.add(node);
569
570       List<StatEdge> lstEdges = node.getAllSuccessorEdges();
571
572       for (; index < lstEdges.size(); index++) {
573         StatEdge edge = lstEdges.get(index);
574         Statement succ = edge.getDestination();
575
576         if (!setVisited.contains(succ) &&
577             (edge.getType() == StatEdge.TYPE_REGULAR || edge.getType() == StatEdge.TYPE_EXCEPTION)) { // TODO: edge filter?
578
579           stackIndex.add(index + 1);
580
581           stackNode.add(succ);
582           stackIndex.add(0);
583
584           break;
585         }
586       }
587
588       if (index == lstEdges.size()) {
589         lst.add(0, node);
590
591         stackNode.removeLast();
592       }
593     }
594   }
595
596
597   private static void addToPostReversePostOrderList(Statement stat, List<? super Statement> lst, HashSet<? super Statement> setVisited) {
598
599     if (setVisited.contains(stat)) { // because of not considered exception edges, s. isExitComponent. Should be rewritten, if possible.
600       return;
601     }
602     setVisited.add(stat);
603
604     for (StatEdge prededge : stat.getEdges(StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION, DIRECTION_BACKWARD)) {
605       Statement pred = prededge.getSource();
606       if (!setVisited.contains(pred)) {
607         addToPostReversePostOrderList(pred, lst, setVisited);
608       }
609     }
610
611     lst.add(0, stat);
612   }
613
614   // *****************************************************************************
615   // getter and setter methods
616   // *****************************************************************************
617
618   public void changeEdgeNode(int direction, StatEdge edge, Statement value) {
619
620     Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
621     Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
622
623     int type = edge.getType();
624
625     int[] arrtypes;
626     if (type == StatEdge.TYPE_EXCEPTION) {
627       arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION};
628     }
629     else {
630       arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type};
631     }
632
633     for (int edgetype : arrtypes) {
634       List<StatEdge> lst = mapEdges.get(edgetype);
635       if (lst != null) {
636         int index = lst.indexOf(edge);
637         if (index >= 0) {
638           mapStates.get(edgetype).set(index, value);
639         }
640       }
641     }
642
643     if (direction == DIRECTION_BACKWARD) {
644       edge.setSource(value);
645     }
646     else {
647       edge.setDestination(value);
648     }
649   }
650
651   public void changeEdgeType(int direction, StatEdge edge, int newtype) {
652
653     int oldtype = edge.getType();
654     if (oldtype == newtype) {
655       return;
656     }
657
658     if (oldtype == StatEdge.TYPE_EXCEPTION || newtype == StatEdge.TYPE_EXCEPTION) {
659       throw new RuntimeException("Invalid edge type!");
660     }
661
662     removeEdgeDirectInternal(direction, edge, oldtype);
663     addEdgeDirectInternal(direction, edge, newtype);
664
665     if (direction == DIRECTION_FORWARD) {
666       edge.getDestination().changeEdgeType(DIRECTION_BACKWARD, edge, newtype);
667     }
668
669     edge.setType(newtype);
670   }
671
672
673   private List<StatEdge> getEdges(int type, int direction) {
674
675     Map<Integer, List<StatEdge>> map = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
676
677     List<StatEdge> res;
678     if ((type & (type - 1)) == 0) {
679       res = map.get(type);
680       res = res == null ? new ArrayList<>() : new ArrayList<>(res);
681     }
682     else {
683       res = new ArrayList<>();
684       for (int edgetype : StatEdge.TYPES) {
685         if ((type & edgetype) != 0) {
686           List<StatEdge> lst = map.get(edgetype);
687           if (lst != null) {
688             res.addAll(lst);
689           }
690         }
691       }
692     }
693
694     return res;
695   }
696
697   public List<Statement> getNeighbours(int type, int direction) {
698
699     Map<Integer, List<Statement>> map = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
700
701     List<Statement> res;
702     if ((type & (type - 1)) == 0) {
703       res = map.get(type);
704       res = res == null ? new ArrayList<>() : new ArrayList<>(res);
705     }
706     else {
707       res = new ArrayList<>();
708       for (int edgetype : StatEdge.TYPES) {
709         if ((type & edgetype) != 0) {
710           List<Statement> lst = map.get(edgetype);
711           if (lst != null) {
712             res.addAll(lst);
713           }
714         }
715       }
716     }
717
718     return res;
719   }
720
721   public Set<Statement> getNeighboursSet(int type, int direction) {
722     return new HashSet<>(getNeighbours(type, direction));
723   }
724
725   public List<StatEdge> getSuccessorEdges(int type) {
726     return getEdges(type, DIRECTION_FORWARD);
727   }
728
729   public List<StatEdge> getPredecessorEdges(int type) {
730     return getEdges(type, DIRECTION_BACKWARD);
731   }
732
733   public List<StatEdge> getAllSuccessorEdges() {
734     return getEdges(STATEDGE_ALL, DIRECTION_FORWARD);
735   }
736
737   public List<StatEdge> getAllPredecessorEdges() {
738     return getEdges(STATEDGE_ALL, DIRECTION_BACKWARD);
739   }
740
741   public Statement getFirst() {
742     return first;
743   }
744
745   public void setFirst(Statement first) {
746     this.first = first;
747   }
748
749   public Statement getPost() {
750     return post;
751   }
752
753   public VBStyleCollection<Statement, Integer> getStats() {
754     return stats;
755   }
756
757   public int getLastBasicType() {
758     return lastBasicType;
759   }
760
761   public HashSet<Statement> getContinueSet() {
762     return continueSet;
763   }
764
765   public boolean isContainsMonitorExit() {
766     return containsMonitorExit;
767   }
768
769   public boolean isMonitorEnter() {
770     return isMonitorEnter;
771   }
772
773   public BasicBlockStatement getBasichead() {
774     if (type == TYPE_BASIC_BLOCK) {
775       return (BasicBlockStatement)this;
776     }
777     else {
778       return first.getBasichead();
779     }
780   }
781
782   public boolean isLabeled() {
783
784     for (StatEdge edge : labelEdges) {
785       if (edge.labeled && edge.explicit) {  // FIXME: consistent setting
786         return true;
787       }
788     }
789     return false;
790   }
791
792   public boolean hasBasicSuccEdge() {
793
794     // FIXME: default switch
795
796     return type == TYPE_BASIC_BLOCK || (type == TYPE_IF &&
797                                                         ((IfStatement)this).iftype == IfStatement.IFTYPE_IF) ||
798            (type == TYPE_DO && ((DoStatement)this).getLoopType() != LoopType.DO);
799   }
800
801
802   public Statement getParent() {
803     return parent;
804   }
805
806   public void setParent(Statement parent) {
807     this.parent = parent;
808   }
809
810   public HashSet<StatEdge> getLabelEdges() {  // FIXME: why HashSet?
811     return labelEdges;
812   }
813
814   public List<Exprent> getVarDefinitions() {
815     return varDefinitions;
816   }
817
818   public List<Exprent> getExprents() {
819     return exprents;
820   }
821
822   public void setExprents(List<Exprent> exprents) {
823     this.exprents = exprents;
824   }
825
826   public boolean isCopied() {
827     return copied;
828   }
829
830   public void setCopied(boolean copied) {
831     this.copied = copied;
832   }
833
834   // helper methods
835   public String toString() {
836     return id.toString();
837   }
838
839   // *****************************************************************************
840   // IMatchable implementation
841   // *****************************************************************************
842
843   @Override
844   public IMatchable findObject(MatchNode matchNode, int index) {
845     int node_type = matchNode.getType();
846
847     if (node_type == MatchNode.MATCHNODE_STATEMENT && !this.stats.isEmpty()) {
848       String position = (String)matchNode.getRuleValue(MatchProperties.STATEMENT_POSITION);
849       if (position != null) {
850         if (position.matches("-?\\d+")) {
851           return this.stats.get((this.stats.size() + Integer.parseInt(position)) % this.stats.size()); // care for negative positions
852         }
853       }
854       else if (index < this.stats.size()) { // use 'index' parameter
855         return this.stats.get(index);
856       }
857     }
858     else if (node_type == MatchNode.MATCHNODE_EXPRENT && this.exprents != null && !this.exprents.isEmpty()) {
859       String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION);
860       if (position != null) {
861         if (position.matches("-?\\d+")) {
862           return this.exprents.get((this.exprents.size() + Integer.parseInt(position)) % this.exprents.size()); // care for negative positions
863         }
864       }
865       else if (index < this.exprents.size()) { // use 'index' parameter
866         return this.exprents.get(index);
867       }
868     }
869
870     return null;
871   }
872
873   @Override
874   public boolean match(MatchNode matchNode, MatchEngine engine) {
875     if (matchNode.getType() != MatchNode.MATCHNODE_STATEMENT) {
876       return false;
877     }
878
879     for (Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {
880       switch (rule.getKey()) {
881         case STATEMENT_TYPE:
882           if (this.type != (Integer)rule.getValue().value) {
883             return false;
884           }
885           break;
886         case STATEMENT_STATSIZE:
887           if (this.stats.size() != (Integer)rule.getValue().value) {
888             return false;
889           }
890           break;
891         case STATEMENT_EXPRSIZE:
892           int exprsize = (Integer)rule.getValue().value;
893           if (exprsize == -1) {
894             if (this.exprents != null) {
895               return false;
896             }
897           }
898           else {
899             if (this.exprents == null || this.exprents.size() != exprsize) {
900               return false;
901             }
902           }
903           break;
904         case STATEMENT_RET:
905           if (!engine.checkAndSetVariableValue((String)rule.getValue().value, this)) {
906             return false;
907           }
908           break;
909       }
910     }
911
912     return true;
913   }
914 }