001package daikon; 002 003import java.io.Serializable; 004import java.util.ArrayList; 005import java.util.List; 006import org.checkerframework.checker.lock.qual.GuardSatisfied; 007import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; 008import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 009import org.checkerframework.checker.nullness.qual.Nullable; 010import org.checkerframework.dataflow.qual.Pure; 011import org.checkerframework.dataflow.qual.SideEffectFree; 012 013/** 014 * A VarComparabilityImplicit is an arbitrary integer, and comparisons succeed exactly if the two 015 * integers are equal, except that negative integers compare equal to everything. Alternately, for 016 * an array variable, a VarComparabilityImplicit may separately indicate comparabilities for the 017 * elements and indices. 018 * 019 * <pre> 020 * VarComparabilityImplicit ::= int 021 * | VarComparabilityImplicit "[" int "]" 022 * </pre> 023 * 024 * <p>This is called "implicit" because the comparability objects do not refer to one another or 025 * refer directly to variables; whether two variables are comparable depends on their comparability 026 * objects. Implicit comparability has the flavor of types in programming languages. 027 * 028 * <p>Soon, this will probably be modified to permit the group identifiers to be arbitrary strings 029 * (not containing square brackets) instead of arbitrary integers. 030 */ 031public final class VarComparabilityImplicit extends VarComparability implements Serializable { 032 static final long serialVersionUID = 20020122L; 033 034 /** The number that indicates which comparable set the VarInfo belongs to. */ 035 int base; 036 037 /** indexTypes[0] is comparability of the first index of this array. */ 038 // null only for the "unknown" type?? 039 VarComparabilityImplicit @Nullable [] indexTypes; 040 041 /** Indicates how many of the indices are in use; there may be more indices than this. */ 042 int dimensions; 043 044 private @MonotonicNonNull VarComparabilityImplicit cached_element_type; 045 046 public static final VarComparabilityImplicit unknown = new VarComparabilityImplicit(-3, null, 0); 047 048 private VarComparabilityImplicit( 049 int base, VarComparabilityImplicit @Nullable [] indexTypes, int dimensions) { 050 this.base = base; 051 this.indexTypes = indexTypes; 052 this.dimensions = dimensions; 053 } 054 055 @Pure 056 @Override 057 public int hashCode(@GuardSatisfied VarComparabilityImplicit this) { 058 if (base < 0) { 059 // This is equals() to everything 060 return -1; 061 } 062 if (dimensions > 0) { 063 return (indexType(dimensions - 1).hashCode() << 4) ^ elementType().hashCode(); 064 } 065 return base; 066 } 067 068 @EnsuresNonNullIf(result = true, expression = "#1") 069 @Pure 070 @Override 071 public boolean equals( 072 @GuardSatisfied VarComparabilityImplicit this, @GuardSatisfied @Nullable Object o) { 073 if (!(o instanceof VarComparabilityImplicit)) { 074 return false; 075 } 076 return equalsVarComparabilityImplicit((VarComparabilityImplicit) o); 077 } 078 079 @EnsuresNonNullIf(result = true, expression = "#1") 080 @Pure 081 public boolean equalsVarComparabilityImplicit( 082 @GuardSatisfied VarComparabilityImplicit this, @GuardSatisfied VarComparabilityImplicit o) { 083 return equality_set_ok(o); 084 } 085 086 public boolean baseAlwayscomparable() { 087 return (base < 0); 088 } 089 090 @Pure 091 @Override 092 public boolean alwaysComparable(@GuardSatisfied VarComparabilityImplicit this) { 093 return (dimensions == 0) && (base < 0); 094 } 095 096 static VarComparabilityImplicit parse(String rep, @Nullable ProglangType vartype) { 097 // String rep_ = rep; // for debugging 098 099 List<String> dim_reps = new ArrayList<>(); 100 // handle array types 101 while (rep.endsWith("]")) { 102 int openpos = rep.lastIndexOf("["); 103 dim_reps.add(0, rep.substring(openpos + 1, rep.length() - 1)); 104 rep = rep.substring(0, openpos); 105 } 106 int dims = dim_reps.size(); 107 VarComparabilityImplicit[] index_types = new VarComparabilityImplicit[dims]; 108 for (int i = 0; i < dims; i++) { 109 index_types[i] = parse(dim_reps.get(i), null); 110 } 111 try { 112 int base = Integer.parseInt(rep); 113 return new VarComparabilityImplicit(base, index_types, dims); 114 } catch (NumberFormatException e) { 115 throw new IllegalArgumentException(e); 116 } 117 } 118 119 @Override 120 public VarComparability makeAlias() { 121 return this; 122 } 123 124 @Override 125 public VarComparability elementType(@GuardSatisfied VarComparabilityImplicit this) { 126 if (cached_element_type == null) { 127 // When Ajax is modified to output non-atomic info for arrays, this 128 // check will no longer be necessary. 129 if (dimensions > 0) { 130 cached_element_type = new VarComparabilityImplicit(base, indexTypes, dimensions - 1); 131 } else { 132 // COMPARABILITY TEST 133 // System.out.println("Warning: taking element type of non-array comparability."); 134 cached_element_type = unknown; 135 } 136 } 137 return cached_element_type; 138 } 139 140 /** 141 * Determines the comparability of the length of this string. Currently always returns unknown, 142 * but it would be best if string lengths were only comparable with other string lengths (or 143 * perhaps nothing). 144 */ 145 @Override 146 public VarComparability string_length_type() { 147 return unknown; 148 } 149 150 @Pure 151 @Override 152 public VarComparability indexType(@GuardSatisfied VarComparabilityImplicit this, int dim) { 153 // When Ajax is modified to output non-atomic info for arrays, this 154 // check will no longer be necessary. 155 if (dim < dimensions) { 156 assert indexTypes != null : "@AssumeAssertion(nullness): dependent: not the unknown type"; 157 return indexTypes[dim]; 158 } else { 159 return unknown; 160 } 161 } 162 163 @SuppressWarnings("all:purity") // Override the purity checker 164 @Pure 165 static boolean comparable( 166 @GuardSatisfied VarComparabilityImplicit type1, 167 @GuardSatisfied VarComparabilityImplicit type2) { 168 if (type1.alwaysComparable()) { 169 return true; 170 } 171 if (type2.alwaysComparable()) { 172 return true; 173 } 174 if ((type1.dimensions > 0) && (type2.dimensions > 0)) { 175 // Both are arrays 176 return (comparable( 177 type1.indexType(type1.dimensions - 1), type2.indexType(type2.dimensions - 1)) 178 && comparable(type1.elementType(), type2.elementType())); 179 } else if ((type1.dimensions == 0) && (type2.dimensions == 0)) { 180 // Neither is an array. 181 return type1.base == type2.base; 182 } else { 183 // One array, one non-array, and the non-array isn't universally comparable. 184 assert type1.dimensions == 0 || type2.dimensions == 0; 185 return false; 186 } 187 } 188 189 /** 190 * Same as comparable, except that variables that are comparable to everything (negative 191 * comparability value) can't be included in the same equality set as those with positive values. 192 */ 193 @Override 194 public boolean equality_set_ok( 195 @GuardSatisfied VarComparabilityImplicit this, @GuardSatisfied VarComparability other) { 196 197 VarComparabilityImplicit type1 = this; 198 VarComparabilityImplicit type2 = (VarComparabilityImplicit) other; 199 200 if ((type1.dimensions > 0) && (type2.dimensions > 0)) { 201 return (type1 202 .indexType(type1.dimensions - 1) 203 .equality_set_ok(type2.indexType(type2.dimensions - 1)) 204 && type1.elementType().equality_set_ok(type2.elementType())); 205 } 206 207 if ((type1.dimensions == 0) && (type2.dimensions == 0)) { 208 return type1.base == type2.base; 209 } 210 211 // One array, one non-array 212 assert type1.dimensions == 0 || type2.dimensions == 0; 213 return false; 214 } 215 216 // for debugging 217 @SideEffectFree 218 @Override 219 public String toString(@GuardSatisfied VarComparabilityImplicit this) { 220 String result = "" + base; 221 for (int i = 0; i < dimensions; i++) { 222 result += "[" + indexType(i) + "]"; 223 } 224 return result; 225 } 226}