001// "Ppt" stands for "Program point" (but is easier to type). 002 003package daikon; 004 005import daikon.inv.Invariant; // for emptyInvList 006import java.io.Serializable; 007import java.util.ArrayList; 008import java.util.Comparator; 009import java.util.List; 010import java.util.StringJoiner; 011import org.checkerframework.checker.initialization.qual.UnknownInitialization; 012import org.checkerframework.checker.interning.qual.UsesObjectEquals; 013import org.checkerframework.checker.lock.qual.GuardSatisfied; 014import org.checkerframework.checker.nullness.qual.Nullable; 015import org.checkerframework.dataflow.qual.Pure; 016import org.checkerframework.dataflow.qual.SideEffectFree; 017 018// Types of Ppt (program point) objects: 019// Ppt: abstract base class 020// PptTopLevel: pointed to by top-level PptMap object. Contains all variables 021// and all data for those variables. 022// PptConditional: contains only value tuples satisfying some condition. 023// Probably doesn't make sense for parent to be a PptSlice. 024// PptSlice: contains a subset of variables. Probably doesn't contain its 025// own data structure with all the values, but depends on its parent 026// (which may be any type of Ppt except a PptSlice, which wouldn't 027// make good sense). 028// Originally, both PptConditional and PptSlice were called "Views"; but 029// presently (6/2002), only Slices are called Views. 030 031// Ppt is an abstract base class rather than an interface in part because 032// interfaces cannot declare member variables. I suspect that using 033// members directly will be more efficient than calling accessor 034// functions such as num_vars() and var_info_iterator(). 035 036// The common interface for all Ppt objects. 037@UsesObjectEquals 038public abstract class Ppt implements Serializable { 039 static final long serialVersionUID = 20040914L; 040 041 // Not final: modified by PptTopLevel.addVarInfos (which is called by 042 // Daikon.create_orig_vars and PptTopLevel.create_derived_variables) 043 // and also by PptSlice0.makeFakePrestate. 044 public VarInfo[] var_infos; 045 046 protected Ppt(VarInfo[] var_infos) { 047 this.var_infos = var_infos; 048 } 049 050 // The "name" and "ppt_name" fields were moved to PptTopLevel: they take 051 // up too much space in PptSlice objects. 052 // This is safe if the receiver is @UnknownInitialization(PptTopLevel.class) OR 053 // @UnknownInitialization(PptSlice.class), but annotations cannot express that. 054 public abstract String name(@GuardSatisfied @UnknownInitialization(PptTopLevel.class) Ppt this); 055 056 /** Trim the collections used in this Ppt. */ 057 public void trimToSize() { 058 for (VarInfo vi : var_infos) { 059 vi.trimToSize(); 060 } 061 } 062 063 protected static final List<Invariant> emptyInvList = new ArrayList<>(); 064 065 /** Returns a string rep of the specified variable names. */ 066 @SuppressWarnings("all:purity") // Impure side effects do not escape (string creation) 067 @SideEffectFree 068 public static String varNames(VarInfo[] infos) { 069 StringJoiner sj = new StringJoiner(", ", "(", ")"); 070 if (infos.length == 0) { 071 sj.add("<implication slice>"); 072 } else { 073 for (VarInfo vi : infos) { 074 sj.add(vi.name()); 075 } 076 } 077 return sj.toString(); 078 } 079 080 /** Return a string representation of the variable names. */ 081 @SideEffectFree 082 public String varNames(@GuardSatisfied @UnknownInitialization(Ppt.class) Ppt this) { 083 return varNames(var_infos); 084 } 085 086 /** 087 * Returns the varinfo_index of the variable whose name is varname. Returns -1 if there is no such 088 * variable. 089 */ 090 @Pure 091 public int indexOf(@UnknownInitialization(Ppt.class) Ppt this, String varname) { 092 for (int i = 0; i < var_infos.length; i++) { 093 if (var_infos[i].name().equals(varname)) { 094 return i; 095 } 096 } 097 return -1; 098 } 099 100 /** Returns the VarInfo with the specified name. Null if the name is not found. */ 101 @Pure 102 public @Nullable VarInfo find_var_by_name( 103 @UnknownInitialization(Ppt.class) Ppt this, String varname) { 104 // System.out.printf("Ppt.find_var_by_name(%s): %s%n", varname, this); 105 int i = indexOf(varname); 106 if (i == -1) { 107 if (varname.contains("[]")) { 108 return find_var_by_name(varname.replace("[]", "[..]")); 109 } 110 // System.out.printf("Ppt.find_var_by_name: Didn't find %s or %s in %s%n", varname, 111 // varname.replace ("[]", "[..]"), this); 112 return null; 113 } else { 114 return var_infos[i]; 115 } 116 } 117 118 public boolean containsVar(VarInfo vi) { 119 // There's gotta be a faster way of doing this. I don't want to 120 // use a HashSet for var_infos because various things clobber 121 // this.var_infos. 122 for (VarInfo elt : var_infos) { 123 if (elt == vi) { 124 return true; 125 } 126 } 127 return false; 128 } 129 130 // It might make more sense to put the sorting into 131 // PptMap.sortedIterator(), for example, but it's in here for now 132 133 // Check if o1 and o2 are both main exits (combined or only exits) 134 // If so, compare their name without the EXIT[line] 135 // If the name is the same, return 0, otherwise 136 // Orders ppts by the name, except . and : are swapped 137 // so that Foo:::OBJECT and Foo:::CLASS are processed before Foo.method. 138 public static final class NameComparator implements Comparator<PptTopLevel> { 139 @Pure 140 @Override 141 public int compare(PptTopLevel p1, PptTopLevel p2) { 142 if (p1 == p2) { 143 return 0; 144 } 145 146 String name1 = p1.name(); 147 String name2 = p2.name(); 148 149 String swapped1 = swap(name1, '.', ':'); 150 String swapped2 = swap(name2, '.', ':'); 151 152 return swapped1.compareTo(swapped2); 153 } 154 155 static String swap(String s, char a, char b) { 156 final char magic = '\255'; 157 return s.replace(a, magic).replace(b, a).replace(magic, b); 158 } 159 } 160}