IDEA-285172 - [decompiler] - StrongConnectivityHelper refactoring
[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       lstexits = new StrongConnectivityHelper(this).getExitReps();
443     }
444
445     HashSet<Statement> setVisited = new HashSet<>();
446
447     for (Statement exit : lstexits) {
448       addToPostReversePostOrderList(exit, res, setVisited);
449     }
450
451     if (res.size() != stats.size()) {
452       throw new RuntimeException("computing post reverse post order failed!");
453     }
454
455     return res;
456   }
457
458   public boolean containsStatement(Statement stat) {
459     return this == stat || containsStatementStrict(stat);
460   }
461
462   public boolean containsStatementStrict(Statement stat) {
463     if (stats.contains(stat)) {
464       return true;
465     }
466
467     for (Statement st : stats) {
468       if (st.containsStatementStrict(stat)) {
469         return true;
470       }
471     }
472
473     return false;
474   }
475
476   public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {
477     throw new RuntimeException("not implemented");
478   }
479
480   // TODO: make obsolete and remove
481   public List<Object> getSequentialObjects() {
482     return new ArrayList<>(stats);
483   }
484
485   public void initExprents() {
486     // do nothing
487   }
488
489   public void replaceExprent(Exprent oldexpr, Exprent newexpr) {
490     // do nothing
491   }
492
493   public Statement getSimpleCopy() {
494     throw new RuntimeException("not implemented");
495   }
496
497   public void initSimpleCopy() {
498     if (!stats.isEmpty()) {
499       first = stats.get(0);
500     }
501   }
502
503   public void replaceStatement(Statement oldstat, Statement newstat) {
504
505     for (StatEdge edge : oldstat.getAllPredecessorEdges()) {
506       oldstat.removePredecessor(edge);
507       edge.getSource().changeEdgeNode(DIRECTION_FORWARD, edge, newstat);
508       newstat.addPredecessor(edge);
509     }
510
511     for (StatEdge edge : oldstat.getAllSuccessorEdges()) {
512       oldstat.removeSuccessor(edge);
513       edge.setSource(newstat);
514       newstat.addSuccessor(edge);
515     }
516
517     int statindex = stats.getIndexByKey(oldstat.id);
518     stats.removeWithKey(oldstat.id);
519     stats.addWithKeyAndIndex(statindex, newstat, newstat.id);
520
521     newstat.setParent(this);
522     newstat.post = oldstat.post;
523
524     if (first == oldstat) {
525       first = newstat;
526     }
527
528     List<StatEdge> lst = new ArrayList<>(oldstat.getLabelEdges());
529
530     for (int i = lst.size() - 1; i >= 0; i--) {
531       StatEdge edge = lst.get(i);
532       if (edge.getSource() != newstat) {
533         newstat.addLabeledEdge(edge);
534       }
535       else {
536         if (this == edge.getDestination() || this.containsStatementStrict(edge.getDestination())) {
537           edge.closure = null;
538         }
539         else {
540           this.addLabeledEdge(edge);
541         }
542       }
543     }
544
545     oldstat.getLabelEdges().clear();
546   }
547
548
549   // *****************************************************************************
550   // private methods
551   // *****************************************************************************
552
553   private static void addToReversePostOrderListIterative(Statement root, List<? super Statement> lst) {
554
555     LinkedList<Statement> stackNode = new LinkedList<>();
556     LinkedList<Integer> stackIndex = new LinkedList<>();
557     HashSet<Statement> setVisited = new HashSet<>();
558
559     stackNode.add(root);
560     stackIndex.add(0);
561
562     while (!stackNode.isEmpty()) {
563
564       Statement node = stackNode.getLast();
565       int index = stackIndex.removeLast();
566
567       setVisited.add(node);
568
569       List<StatEdge> lstEdges = node.getAllSuccessorEdges();
570
571       for (; index < lstEdges.size(); index++) {
572         StatEdge edge = lstEdges.get(index);
573         Statement succ = edge.getDestination();
574
575         if (!setVisited.contains(succ) &&
576             (edge.getType() == StatEdge.TYPE_REGULAR || edge.getType() == StatEdge.TYPE_EXCEPTION)) { // TODO: edge filter?
577
578           stackIndex.add(index + 1);
579
580           stackNode.add(succ);
581           stackIndex.add(0);
582
583           break;
584         }
585       }
586
587       if (index == lstEdges.size()) {
588         lst.add(0, node);
589
590         stackNode.removeLast();
591       }
592     }
593   }
594
595
596   private static void addToPostReversePostOrderList(Statement stat, List<? super Statement> lst, HashSet<? super Statement> setVisited) {
597
598     if (setVisited.contains(stat)) { // because of not considered exception edges, s. isExitComponent. Should be rewritten, if possible.
599       return;
600     }
601     setVisited.add(stat);
602
603     for (StatEdge prededge : stat.getEdges(StatEdge.TYPE_REGULAR | StatEdge.TYPE_EXCEPTION, DIRECTION_BACKWARD)) {
604       Statement pred = prededge.getSource();
605       if (!setVisited.contains(pred)) {
606         addToPostReversePostOrderList(pred, lst, setVisited);
607       }
608     }
609
610     lst.add(0, stat);
611   }
612
613   // *****************************************************************************
614   // getter and setter methods
615   // *****************************************************************************
616
617   public void changeEdgeNode(int direction, StatEdge edge, Statement value) {
618
619     Map<Integer, List<StatEdge>> mapEdges = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
620     Map<Integer, List<Statement>> mapStates = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
621
622     int type = edge.getType();
623
624     int[] arrtypes;
625     if (type == StatEdge.TYPE_EXCEPTION) {
626       arrtypes = new int[]{STATEDGE_ALL, StatEdge.TYPE_EXCEPTION};
627     }
628     else {
629       arrtypes = new int[]{STATEDGE_ALL, STATEDGE_DIRECT_ALL, type};
630     }
631
632     for (int edgetype : arrtypes) {
633       List<StatEdge> lst = mapEdges.get(edgetype);
634       if (lst != null) {
635         int index = lst.indexOf(edge);
636         if (index >= 0) {
637           mapStates.get(edgetype).set(index, value);
638         }
639       }
640     }
641
642     if (direction == DIRECTION_BACKWARD) {
643       edge.setSource(value);
644     }
645     else {
646       edge.setDestination(value);
647     }
648   }
649
650   public void changeEdgeType(int direction, StatEdge edge, int newtype) {
651
652     int oldtype = edge.getType();
653     if (oldtype == newtype) {
654       return;
655     }
656
657     if (oldtype == StatEdge.TYPE_EXCEPTION || newtype == StatEdge.TYPE_EXCEPTION) {
658       throw new RuntimeException("Invalid edge type!");
659     }
660
661     removeEdgeDirectInternal(direction, edge, oldtype);
662     addEdgeDirectInternal(direction, edge, newtype);
663
664     if (direction == DIRECTION_FORWARD) {
665       edge.getDestination().changeEdgeType(DIRECTION_BACKWARD, edge, newtype);
666     }
667
668     edge.setType(newtype);
669   }
670
671
672   private List<StatEdge> getEdges(int type, int direction) {
673
674     Map<Integer, List<StatEdge>> map = direction == DIRECTION_BACKWARD ? mapPredEdges : mapSuccEdges;
675
676     List<StatEdge> res;
677     if ((type & (type - 1)) == 0) {
678       res = map.get(type);
679       res = res == null ? new ArrayList<>() : new ArrayList<>(res);
680     }
681     else {
682       res = new ArrayList<>();
683       for (int edgetype : StatEdge.TYPES) {
684         if ((type & edgetype) != 0) {
685           List<StatEdge> lst = map.get(edgetype);
686           if (lst != null) {
687             res.addAll(lst);
688           }
689         }
690       }
691     }
692
693     return res;
694   }
695
696   public List<Statement> getNeighbours(int type, int direction) {
697
698     Map<Integer, List<Statement>> map = direction == DIRECTION_BACKWARD ? mapPredStates : mapSuccStates;
699
700     List<Statement> res;
701     if ((type & (type - 1)) == 0) {
702       res = map.get(type);
703       res = res == null ? new ArrayList<>() : new ArrayList<>(res);
704     }
705     else {
706       res = new ArrayList<>();
707       for (int edgetype : StatEdge.TYPES) {
708         if ((type & edgetype) != 0) {
709           List<Statement> lst = map.get(edgetype);
710           if (lst != null) {
711             res.addAll(lst);
712           }
713         }
714       }
715     }
716
717     return res;
718   }
719
720   public Set<Statement> getNeighboursSet(int type, int direction) {
721     return new HashSet<>(getNeighbours(type, direction));
722   }
723
724   public List<StatEdge> getSuccessorEdges(int type) {
725     return getEdges(type, DIRECTION_FORWARD);
726   }
727
728   public List<StatEdge> getPredecessorEdges(int type) {
729     return getEdges(type, DIRECTION_BACKWARD);
730   }
731
732   public List<StatEdge> getAllSuccessorEdges() {
733     return getEdges(STATEDGE_ALL, DIRECTION_FORWARD);
734   }
735
736   public List<StatEdge> getAllPredecessorEdges() {
737     return getEdges(STATEDGE_ALL, DIRECTION_BACKWARD);
738   }
739
740   public Statement getFirst() {
741     return first;
742   }
743
744   public void setFirst(Statement first) {
745     this.first = first;
746   }
747
748   public Statement getPost() {
749     return post;
750   }
751
752   public VBStyleCollection<Statement, Integer> getStats() {
753     return stats;
754   }
755
756   public int getLastBasicType() {
757     return lastBasicType;
758   }
759
760   public HashSet<Statement> getContinueSet() {
761     return continueSet;
762   }
763
764   public boolean isContainsMonitorExit() {
765     return containsMonitorExit;
766   }
767
768   public boolean isMonitorEnter() {
769     return isMonitorEnter;
770   }
771
772   public BasicBlockStatement getBasichead() {
773     if (type == TYPE_BASIC_BLOCK) {
774       return (BasicBlockStatement)this;
775     }
776     else {
777       return first.getBasichead();
778     }
779   }
780
781   public boolean isLabeled() {
782
783     for (StatEdge edge : labelEdges) {
784       if (edge.labeled && edge.explicit) {  // FIXME: consistent setting
785         return true;
786       }
787     }
788     return false;
789   }
790
791   public boolean hasBasicSuccEdge() {
792
793     // FIXME: default switch
794
795     return type == TYPE_BASIC_BLOCK || (type == TYPE_IF &&
796                                                         ((IfStatement)this).iftype == IfStatement.IFTYPE_IF) ||
797            (type == TYPE_DO && ((DoStatement)this).getLoopType() != LoopType.DO);
798   }
799
800
801   public Statement getParent() {
802     return parent;
803   }
804
805   public void setParent(Statement parent) {
806     this.parent = parent;
807   }
808
809   public HashSet<StatEdge> getLabelEdges() {  // FIXME: why HashSet?
810     return labelEdges;
811   }
812
813   public List<Exprent> getVarDefinitions() {
814     return varDefinitions;
815   }
816
817   public List<Exprent> getExprents() {
818     return exprents;
819   }
820
821   public void setExprents(List<Exprent> exprents) {
822     this.exprents = exprents;
823   }
824
825   public boolean isCopied() {
826     return copied;
827   }
828
829   public void setCopied(boolean copied) {
830     this.copied = copied;
831   }
832
833   // helper methods
834   public String toString() {
835     return id.toString();
836   }
837
838   // *****************************************************************************
839   // IMatchable implementation
840   // *****************************************************************************
841
842   @Override
843   public IMatchable findObject(MatchNode matchNode, int index) {
844     int node_type = matchNode.getType();
845
846     if (node_type == MatchNode.MATCHNODE_STATEMENT && !this.stats.isEmpty()) {
847       String position = (String)matchNode.getRuleValue(MatchProperties.STATEMENT_POSITION);
848       if (position != null) {
849         if (position.matches("-?\\d+")) {
850           return this.stats.get((this.stats.size() + Integer.parseInt(position)) % this.stats.size()); // care for negative positions
851         }
852       }
853       else if (index < this.stats.size()) { // use 'index' parameter
854         return this.stats.get(index);
855       }
856     }
857     else if (node_type == MatchNode.MATCHNODE_EXPRENT && this.exprents != null && !this.exprents.isEmpty()) {
858       String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION);
859       if (position != null) {
860         if (position.matches("-?\\d+")) {
861           return this.exprents.get((this.exprents.size() + Integer.parseInt(position)) % this.exprents.size()); // care for negative positions
862         }
863       }
864       else if (index < this.exprents.size()) { // use 'index' parameter
865         return this.exprents.get(index);
866       }
867     }
868
869     return null;
870   }
871
872   @Override
873   public boolean match(MatchNode matchNode, MatchEngine engine) {
874     if (matchNode.getType() != MatchNode.MATCHNODE_STATEMENT) {
875       return false;
876     }
877
878     for (Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {
879       switch (rule.getKey()) {
880         case STATEMENT_TYPE:
881           if (this.type != (Integer)rule.getValue().value) {
882             return false;
883           }
884           break;
885         case STATEMENT_STATSIZE:
886           if (this.stats.size() != (Integer)rule.getValue().value) {
887             return false;
888           }
889           break;
890         case STATEMENT_EXPRSIZE:
891           int exprsize = (Integer)rule.getValue().value;
892           if (exprsize == -1) {
893             if (this.exprents != null) {
894               return false;
895             }
896           }
897           else {
898             if (this.exprents == null || this.exprents.size() != exprsize) {
899               return false;
900             }
901           }
902           break;
903         case STATEMENT_RET:
904           if (!engine.checkAndSetVariableValue((String)rule.getValue().value, this)) {
905             return false;
906           }
907           break;
908       }
909     }
910
911     return true;
912   }
913 }