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