001package daikon.chicory; 002 003import daikon.plumelib.util.EntryReader; 004import java.io.File; 005import java.io.IOException; 006import java.util.HashMap; 007import java.util.LinkedHashMap; 008import java.util.Scanner; 009import org.checkerframework.checker.lock.qual.GuardSatisfied; 010import org.checkerframework.checker.nullness.qual.Nullable; 011import org.checkerframework.dataflow.qual.SideEffectFree; 012import org.checkerframework.dataflow.qual.TerminatesExecution; 013 014/** 015 * Reads declaration files and provides methods to access the information within them. A declaration 016 * file consists of a number of program points and the variables for each program point. 017 */ 018public class DeclReader { 019 020 /** Map from ppt name to corresponding DeclPpt. */ 021 public HashMap<String, DeclPpt> ppts = new LinkedHashMap<>(); 022 023 /** Information about variables within a program point. */ 024 public static class DeclVarInfo { 025 public String name; 026 public String type; 027 public String rep_type; 028 public String comparability; 029 public int index; 030 031 public DeclVarInfo(String name, String type, String rep_type, String comparability, int index) { 032 this.name = name; 033 this.type = type; 034 this.rep_type = rep_type; 035 this.comparability = comparability; 036 this.index = index; 037 } 038 039 /** 040 * Returns the variable's name. 041 * 042 * @return the variable's name 043 */ 044 public String get_name() { 045 return name; 046 } 047 048 /** 049 * Returns the comparability value from the decl file. 050 * 051 * @return the comparability value 052 */ 053 public String get_comparability() { 054 return comparability; 055 } 056 057 @SideEffectFree 058 @Override 059 public String toString(@GuardSatisfied DeclVarInfo this) { 060 return String.format("%s [%s] %s", type, rep_type, name); 061 } 062 } 063 064 /** 065 * Information about the program point that is contained in the decl file. This consists of the 066 * ppt name and a list of the declared variables. 067 */ 068 public static class DeclPpt { 069 /** Program point name. */ 070 public String name; 071 072 /** Map from variable name to corresponding DeclVarInfo. */ 073 public HashMap<String, DeclVarInfo> vars = new LinkedHashMap<>(); 074 075 /** 076 * DeclPpt constructor. 077 * 078 * @param name program point name 079 */ 080 public DeclPpt(String name) { 081 this.name = name; 082 } 083 084 /** 085 * Read a single variable declaration from decl_file. The file must be positioned immediately 086 * before the variable name. 087 * 088 * @param decl_file where to read data from 089 * @return DeclVarInfo for the program point variable 090 * @throws IOException if there is trouble reading the file 091 */ 092 public DeclVarInfo read_var(EntryReader decl_file) throws java.io.IOException { 093 094 String firstLine = decl_file.readLine(); 095 if (firstLine == null) { 096 reportFileError(decl_file, "Expected \"variable <VARNAME>\", found end of file"); 097 } 098 Scanner scanner = new Scanner(firstLine); 099 if (!(scanner.hasNext() && scanner.next().equals("variable") && scanner.hasNext())) { 100 reportFileError(decl_file, "Expected \"variable <VARNAME>\", found \"" + firstLine + "\""); 101 } 102 String varName = scanner.next(); 103 104 String type = null; 105 String rep_type = null; 106 String comparability = null; 107 108 // read variable data records until next variable or blank line 109 String record = decl_file.readLine(); 110 while ((record != null) && (record.length() != 0)) { 111 scanner = new Scanner(record); 112 String token = scanner.next(); 113 if (token.equals("variable")) { 114 break; 115 } else if (token.equals("dec-type")) { 116 if (!scanner.hasNext()) { 117 reportFileError(decl_file, "\"dec-type\" not followed by a type"); 118 } 119 type = scanner.next(); 120 } else if (token.equals("rep-type")) { 121 if (!scanner.hasNext()) { 122 reportFileError(decl_file, "\"rep-type\" not followed by a type"); 123 } 124 rep_type = scanner.next(); 125 } else if (token.equals("comparability")) { 126 if (!scanner.hasNext()) { 127 reportFileError(decl_file, "\"comparability\" not followed by a comparability-type"); 128 } 129 comparability = scanner.next(); 130 } 131 // Chicory ignores all other record types (such as flags and enclosing-var) as they are not 132 // needed to calculate comparability values. 133 record = decl_file.readLine(); 134 } 135 // push back the variable or blank line record 136 if (record != null) { 137 decl_file.putback(record); 138 } 139 140 if (type == null) { 141 reportFileError(decl_file, "No type for variable " + varName); 142 } 143 if (rep_type == null) { 144 reportFileError(decl_file, "No rep-type for variable " + varName); 145 } 146 if (comparability == null) { 147 reportFileError(decl_file, "No comparability for variable " + varName); 148 } 149 150 // I don't see the point of this interning. No code seems to take 151 // advantage of it. Is it just for space? -MDE 152 DeclVarInfo var = 153 new DeclVarInfo( 154 varName.intern(), 155 type.intern(), 156 rep_type.intern(), 157 comparability.intern(), 158 vars.size()); 159 vars.put(varName, var); 160 return var; 161 } 162 163 /** 164 * Returns the DeclVarInfo named var_name or null if it doesn't exist. 165 * 166 * @param var_name a variable name 167 * @return DeclVarInfo for the given variable 168 */ 169 public @Nullable DeclVarInfo find_var(String var_name) { 170 return vars.get(var_name); 171 } 172 173 /** 174 * Returns the ppt name. 175 * 176 * @return the program point name 177 */ 178 public String get_name() { 179 return name; 180 } 181 182 /** 183 * Returns the name without the :::EXIT, :::ENTER, etc. 184 * 185 * @return the program point name 186 */ 187 public String get_short_name() { 188 return name.replaceFirst(":::.*", ""); 189 } 190 191 @SideEffectFree 192 @Override 193 public String toString(@GuardSatisfied DeclPpt this) { 194 return name; 195 } 196 } 197 198 /** Create a new DeclReader. */ 199 public DeclReader() {} 200 201 /** 202 * Read declarations from the specified pathname. 203 * 204 * @param pathname a File for reading data 205 * @throws IOException if there is trouble reading the file 206 */ 207 public void read(File pathname) throws IOException { 208 209 // have caller deal with FileNotFound 210 211 try (EntryReader decl_file = new EntryReader(pathname, "^(//|#).*", null)) { 212 for (String line = decl_file.readLine(); line != null; line = decl_file.readLine()) { 213 // Skip all input until we find a ppt. 214 if (!line.startsWith("ppt ")) { 215 continue; 216 } 217 decl_file.putback(line); 218 219 // Read the program point declaration. 220 read_decl(decl_file); 221 } 222 } catch (Exception e) { 223 throw new Error("Error reading comparability decl file " + pathname, e); 224 } 225 } 226 227 /** 228 * Reads a single program point declaration from decl_file. 229 * 230 * @param decl_file EntryReader for reading data 231 * @return the program point declaration 232 * @throws IOException if there is trouble reading the file 233 */ 234 protected DeclPpt read_decl(EntryReader decl_file) throws IOException { 235 236 // Read the name of the program point 237 String firstLine = decl_file.readLine(); 238 if (firstLine == null) { 239 reportFileError(decl_file, "File ends prematurely, expected \"ppt ...\""); 240 } 241 if (!firstLine.startsWith("ppt ")) { 242 reportFileError(decl_file, "Expected \"ppt ...\", found \"" + firstLine + "\""); 243 } 244 String pptname = firstLine.substring(4); // skip "ppt " 245 assert pptname.contains(":::"); 246 DeclPpt ppt = new DeclPpt(pptname); 247 ppts.put(pptname, ppt); 248 249 // Chicory skips the ppt-type record as it is not needed to calculate comparability values. 250 String ppt_type = decl_file.readLine(); 251 if (ppt_type == null) { 252 reportFileError(decl_file, "File terminated prematurely while reading decl for " + ppt); 253 } 254 255 // Read each of the variables in this program point. The variables 256 // are terminated by a blank line. 257 String line = decl_file.readLine(); 258 while ((line != null) && (line.length() != 0)) { 259 if (!line.startsWith("variable ")) { 260 reportFileError(decl_file, "Expected \"variable ...\", found \"" + line + "\""); 261 } 262 decl_file.putback(line); 263 ppt.read_var(decl_file); 264 line = decl_file.readLine(); 265 } 266 267 return ppt; 268 } 269 270 // This can return null. Example: when DynComp is run to compute 271 // comparability information, it produces no information (not even a 272 // declaration) for program points that are never executed. But, Chicory 273 // outputs a declaration for every program point, and this lookup can 274 // fail when using the --comparability-file=... command-line argument 275 // with a file produced by DynComp. 276 public @Nullable DeclPpt find_ppt(String ppt_name) { 277 DeclPpt result = ppts.get(ppt_name); 278 return result; 279 } 280 281 /** 282 * Report an error while reading from an EntryReader, with file name and line number. 283 * 284 * @param er an EntryReader, from which file name and line number are obtained 285 * @param message the error message 286 */ 287 @TerminatesExecution 288 private static void reportFileError(EntryReader er, String message) { 289 throw new Error(message + " at " + er.getFileName() + " line " + er.getLineNumber()); 290 } 291}