001package daikon; 002 003import java.io.LineNumberReader; 004import java.io.ObjectStreamException; 005import java.io.Serializable; 006import java.io.StreamTokenizer; 007import java.io.StringReader; 008import java.util.ArrayList; 009import java.util.HashMap; 010import java.util.HashSet; 011import java.util.LinkedHashSet; 012import java.util.List; 013import org.checkerframework.checker.interning.qual.Interned; 014import org.checkerframework.checker.lock.qual.GuardSatisfied; 015import org.checkerframework.checker.nullness.qual.Nullable; 016import org.checkerframework.checker.signedness.qual.Signed; 017import org.checkerframework.dataflow.qual.Pure; 018import org.checkerframework.dataflow.qual.SideEffectFree; 019import org.plumelib.util.Intern; 020import org.plumelib.util.StringsPlume; 021 022/** 023 * Represents the type of a variable, for its declared type, dtrace file representation, and 024 * internal representations. ProgLangTypes are interned, so they can be == compared. 025 */ 026 027// I could also consider using Class; however: 028// * that ties this to a Java front end, as Class can't represent types of 029// (say) C variables. (not a compelling problem) 030// * that loads the class, which requies that all classes available at 031// run time be available at inference time. (not a compelling problem) 032// * Class does not represent inheritance (but I can do that myself); 033// and see isAssignableFrom, which might do all I need. 034// * Class has no "dimensions" field. isArray() exists, however, as does 035// getComponentType(). I can always determine the number of dimensions 036// from the number of leading "[" in the name, and dimensions is only 037// really needed for parse_value. parse_value can do literal checks and 038// elementType() can use the name, I suppose. Or maybe try to 039// instantiate something, though that seems dicier. 040 041// Problem: this doesn't currently represent inheritance, coercability, or 042// other relationships over the base types. 043 044// integral_types = ("int", "char", "float", "double", "integral", "boolean") 045// known_types = integral_types + ("pointer", "address") 046 047public final @Interned class ProglangType implements Serializable { 048 // We are Serializable, so we specify a version to allow changes to 049 // method signatures without breaking serialization. If you add or 050 // remove fields, you should change this number to the current date. 051 static final long serialVersionUID = 20020122L; 052 053 /** Maps from a base type name to its ProglangTypes and arrays with that base. */ 054 private static HashMap<@Interned String, List<ProglangType>> all_known_types = new HashMap<>(); 055 056 /** 057 * The set of (interned) names of classes that implement java.util.List. For a Java class, this is 058 * a {@code @BinaryName}, but when Daikon is processing programs written in other languages, it 059 * can be arbitrary. 060 */ 061 public static HashSet<String> list_implementors = new LinkedHashSet<>(); 062 063 /** 064 * If true, treat 32 bit values whose high bit is on, as a negative number (rather than as a 32 065 * bit unsigned). 066 */ 067 public static boolean dkconfig_convert_to_signed = false; 068 069 static { 070 // XXX for now, hard-code these list-implementing types. We should 071 // make the front-end dump the language-specific ones into .decls. 072 list_implementors.add("java.util.List"); 073 list_implementors.add("java.util.AbstractList"); 074 list_implementors.add("java.util.Vector"); 075 list_implementors.add("java.util.ArrayList"); 076 list_implementors.add("java.util.AbstractSequentialList"); 077 list_implementors.add("java.util.LinkedList"); 078 list_implementors.add("java.util.Stack"); 079 } 080 081 // For a Java class, this is a binary name. When Daikon is processing 082 // other languages, the format is arbitrary. 083 private @Interned String base; // interned name of base type 084 085 public @Interned String base() { 086 return base; 087 } 088 089 /** Number of dimensions. Zero for a non-array. */ 090 private int dimensions; 091 092 /** 093 * Return the number of dimensions (zero for a non-array). 094 * 095 * @return the number of dimensions 096 */ 097 public int dimensions() { 098 return dimensions; 099 } 100 101 @Pure 102 public boolean isArray() { 103 return dimensions > 0; 104 } 105 106 /** 107 * No public constructor: use parse() instead to get a canonical representation. basetype should 108 * be interned. 109 * 110 * @param basetype the base type 111 * @param dimensions the number of dimensions 112 */ 113 @SuppressWarnings("super.invocation") // never called twice with the same arguments 114 private ProglangType(@Interned String basetype, int dimensions) { 115 assert basetype == basetype.intern(); 116 this.base = basetype; 117 this.dimensions = dimensions; 118 } 119 120 /** 121 * This can't be a constructor because it returns a canonical representation (that can be compared 122 * with ==), not necessarily a new object. 123 * 124 * @param rep the name of the type, optionally suffixed by (possibly multiple) "[]" 125 */ 126 public static ProglangType parse(String rep) { 127 String new_base = rep; 128 int dims = 0; 129 while (new_base.endsWith("[]")) { 130 dims++; 131 new_base = new_base.substring(0, new_base.length() - 2); 132 } 133 return intern(new_base.intern(), dims); 134 } 135 136 /** 137 * Like parse, but normalizes representation types (such as converting "float" to "double"), in 138 * order to return real file representation types even if the file contains something slightly 139 * different than the prescribed format. 140 */ 141 public static ProglangType rep_parse(String rep) { 142 ProglangType candidate = parse(rep); 143 if ((candidate.base == "address") || (candidate.base == "pointer")) { // interned 144 return intern("hashcode", candidate.dimensions); 145 } else if (candidate.base == "float") { // interned 146 return intern("double", candidate.dimensions); 147 } else if (candidate.base == "string") { // interned 148 return intern("java.lang.String", candidate.dimensions); 149 } else { 150 return candidate; 151 } 152 } 153 154 /** Convert a file representation type to an internal representation type. */ 155 public ProglangType fileTypeToRepType() { 156 if ((base == BASE_HASHCODE) 157 || (base == BASE_BOOLEAN) 158 || (base == BASE_LONG) 159 || (base == BASE_LONG_LONG) 160 || (base == BASE_SHORT)) { 161 return intern(BASE_INT, dimensions); 162 } 163 return this; 164 } 165 166 /** 167 * For serialization; indicates which object to return instead of the one that was just read from 168 * the file. This obviates the need to write a readObject method that interns the interned fields 169 * (just "base"). 170 */ 171 public Object readResolve() throws ObjectStreamException { 172 return intern(base.intern(), dimensions); 173 } 174 175 // Is this necessary? It will be inherited from Object. 176 // @EnsuresNonNullIf(result=true, expression="#1") 177 // public boolean equals(@Nullable Object o) { 178 // return this == o; 179 // } 180 181 // THIS CODE IS A HOT SPOT (~33% of run time) [as of January 2002]. 182 /** 183 * Searches for an array type with the given base and number of dimensions. 184 * 185 * @param t_base the base type; must be interned 186 * @param t_dims the number of dimensions 187 * @return an array type with the given base and number of dimensions, or null 188 */ 189 private static @Nullable ProglangType find(@Interned String t_base, int t_dims) { 190 // Disabled for performance reasons! this assertion is sound though: 191 // assert t_base == t_base.intern(); 192 193 // the string maps us to a vec of all ProglangTypes with that base 194 List<ProglangType> v = all_known_types.get(t_base); 195 if (v == null) { 196 return null; 197 } 198 199 // now search for the right dimension 200 for (ProglangType candidate : v) { 201 if (candidate.dimensions() == t_dims) { 202 return candidate; 203 } 204 } 205 206 return null; 207 } 208 209 // private ProglangType intern() { 210 // return ProglangType.intern(this); 211 // } 212 213 // We shouldn't even get to the point of interning; we should not 214 // have created the ProglangType if it isn't canonical. 215 // private static ProglangType intern(ProglangType t) { 216 // ProglangType candidate = find(t.base, t.dimensions); 217 // if (candidate != null) 218 // return candidate; 219 // all_known_types.add(t); 220 // return t; 221 // } 222 223 private static ProglangType intern(@Interned String t_base, int t_dims) { 224 // Disabled for performance reasons! this assertion is sound though: 225 // assert t_base == t_base.intern(); 226 ProglangType existing = find(t_base, t_dims); 227 if (existing != null) { 228 return existing; 229 } 230 @SuppressWarnings("interning") // test above did not find one, so the new one is interned 231 @Interned ProglangType result = new ProglangType(t_base, t_dims); 232 233 List<ProglangType> v = 234 all_known_types.computeIfAbsent(t_base, __ -> new ArrayList<ProglangType>()); 235 236 v.add(result); 237 238 return result; 239 } 240 241 /** 242 * Returns the type of elements of this. They may themselves be arrays if this is 243 * multidimensional. 244 */ 245 public ProglangType elementType(@GuardSatisfied ProglangType this) { 246 // Presume that if there are no dimensions, this must be a list of 247 // objects. Callers should really find this out from other information 248 // in the variable, but this will old code that relied on the pseudo 249 // dimensions of lists to work 250 if (dimensions == 0) { 251 return OBJECT; 252 } 253 assert base == base.intern() : "Uninterned base " + base; 254 return ProglangType.intern(base, dimensions - 1); 255 } 256 257 // All these variables are public because the way to check the base of an 258 // array is type.base() == ProglangType.BASE_CHAR. 259 260 // Primitive types 261 static final @Interned String BASE_BOOLEAN = "boolean"; 262 static final @Interned String BASE_BYTE = "byte"; 263 static final @Interned String BASE_CHAR = "char"; 264 static final @Interned String BASE_DOUBLE = "double"; 265 static final @Interned String BASE_FLOAT = "float"; 266 static final @Interned String BASE_INT = "int"; 267 static final @Interned String BASE_LONG = "long"; 268 static final @Interned String BASE_LONG_LONG = "long long int"; 269 static final @Interned String BASE_SHORT = "short"; 270 271 // Nonprimitive types 272 static final @Interned String BASE_OBJECT = "java.lang.Object"; 273 static final @Interned String BASE_STRING = "java.lang.String"; 274 static final @Interned String BASE_INTEGER = "java.lang.Integer"; 275 // "hashcode", "address", and "pointer" are identical; 276 // "hashcode" is preferred. 277 static final @Interned String BASE_HASHCODE = "hashcode"; 278 // static final @Interned String BASE_ADDRESS = "address"; 279 // static final @Interned String BASE_POINTER = "pointer"; 280 281 // avoid duplicate allocations 282 // No need for the Integer versions; use Long instead. 283 // static final @Interned Integer IntegerZero = Intern.internedInteger(0); 284 // static final @Interned Integer IntegerOne = Intern.internedInteger(1); 285 static final @Interned Long LongZero = Intern.internedLong(0); 286 static final @Interned Long LongOne = Intern.internedLong(1); 287 static final @Interned Double DoubleZero = Intern.internedDouble(0); 288 static final @Interned Double DoubleNaN = Intern.internedDouble(Double.NaN); 289 static final @Interned Double DoublePositiveInfinity = 290 Intern.internedDouble(Double.POSITIVE_INFINITY); 291 static final @Interned Double DoubleNegativeInfinity = 292 Intern.internedDouble(Double.NEGATIVE_INFINITY); 293 294 /* 295 * Now that all other static initialisers are done, it is safe to 296 * construct some instances, also statically. Note that these are 297 * down here to delay their instantiation until after we have 298 * created (e.g.) the list_implementors object, etc. */ 299 300 // Use == to compare, because ProglangType objects are interned. 301 public static final ProglangType INT = ProglangType.intern("int", 0); 302 public static final ProglangType LONG_PRIMITIVE = ProglangType.intern("long", 0); 303 public static final ProglangType DOUBLE = ProglangType.intern("double", 0); 304 public static final ProglangType CHAR = ProglangType.intern("char", 0); 305 public static final ProglangType STRING = ProglangType.intern("java.lang.String", 0); 306 public static final ProglangType INT_ARRAY = ProglangType.intern("int", 1); 307 public static final ProglangType LONG_PRIMITIVE_ARRAY = ProglangType.intern("long", 1); 308 public static final ProglangType DOUBLE_ARRAY = ProglangType.intern("double", 1); 309 public static final ProglangType CHAR_ARRAY = ProglangType.intern("char", 1); 310 public static final ProglangType STRING_ARRAY = ProglangType.intern("java.lang.String", 1); 311 public static final ProglangType CHAR_ARRAY_ARRAY = ProglangType.intern("char", 2); 312 313 public static final ProglangType INTEGER = ProglangType.intern("java.lang.Integer", 0); 314 public static final ProglangType LONG_OBJECT = ProglangType.intern("java.lang.Long", 0); 315 316 public static final ProglangType OBJECT = ProglangType.intern("java.lang.Object", 0); 317 318 public static final ProglangType BOOLEAN = ProglangType.intern("boolean", 0); 319 public static final ProglangType HASHCODE = ProglangType.intern("hashcode", 0); 320 public static final ProglangType BOOLEAN_ARRAY = ProglangType.intern("boolean", 1); 321 public static final ProglangType HASHCODE_ARRAY = ProglangType.intern("hashcode", 1); 322 323 /** 324 * Like Long.parseLong(), but transform large unsigned longs (as from C's unsigned long long) into 325 * the corresponding negative Java longs. Also handles hex values that begin with 0x. 326 */ 327 @SuppressWarnings("ConstantOverflow") 328 private static long myParseLong(String value) { 329 if ((value.length() == 20 && value.charAt(0) == '1') 330 || (value.length() == 19 331 && value.charAt(0) == '9' 332 && value.compareTo("9223372036854775808") >= 0)) { 333 // Oops, we got a large unsigned long, which Java, having 334 // only signed longs, will refuse to parse. We'll have to 335 // turn it into the corresponding negative long value. 336 String rest; // A substring that will be a valid positive long 337 long subtracted; // The amount we effectively subtracted to make it 338 if (value.length() == 20) { 339 rest = value.substring(1); 340 subtracted = 100000L * 100000 * 100000 * 10000; // 10^19; overflows 341 } else { 342 rest = value.substring(1); 343 subtracted = 9L * 100000 * 100000 * 100000 * 1000; // 9*10^18 344 } 345 return Long.parseLong(rest) + subtracted; 346 } else { 347 long val; 348 if ((value.length() > 2) && (value.charAt(0) == '0') && (value.charAt(1) == 'x')) { 349 val = Long.parseLong(value.substring(2), 16); 350 } else { 351 val = Long.parseLong(value); 352 } 353 // presume that 32 bit values are signed 354 if (dkconfig_convert_to_signed 355 && (((val & 0x80000000L) == 0x80000000L) && ((val & 0xFFFFFFFF00000000L) == 0))) { 356 // long orig = val; 357 val |= 0xFFFFFFFF00000000L; 358 // System.out.printf("Warning: converted %d to %d%n", orig, val); 359 } 360 return val; 361 } 362 } 363 364 /** 365 * Given a string representation of a value (of the type represented by this ProglangType), return 366 * the (canonicalized) interpretation of that value. 367 * 368 * <p>This ProglangType (the method receiver) is assumed to be a representation type, not an 369 * arbitrary type in the underlying programming language. 370 * 371 * <p>If the type is an array and there are any nonsensical elements in the array, the entire 372 * array is considered to be nonsensical (indicated by returning null). This is not really 373 * correct, but it is a reasonable path to take for now. (jhp, Feb 12, 2005) 374 */ 375 public final @Nullable @Interned Object parse_value( 376 String value, LineNumberReader reader, String filename) { 377 // System.out.println(format() + ".parse(\"" + value + "\")"); 378 379 switch (dimensions) { 380 case 0: 381 return parse_value_scalar(value, reader, filename); 382 case 1: 383 return parse_value_array_1d(value, reader, filename); 384 case 2: 385 return parse_value_array_2d(value, reader, filename); 386 default: 387 throw new Error("Can't parse a value of type " + format()); 388 } 389 } 390 391 public final @Nullable @Interned Object parse_value_scalar( 392 String value, LineNumberReader reader, String filename) { 393 // System.out.println(format() + ".parse(\"" + value + "\")"); 394 395 assert dimensions == 0; 396 397 if (base == BASE_STRING) { 398 if (value.equals("null")) { 399 return null; 400 } 401 // assert value.startsWith("\"") && value.endsWith("\""); 402 if (value.startsWith("\"") && value.endsWith("\"")) { 403 value = value.substring(1, value.length() - 1); 404 } else { 405 // Unfortunately, there is not a convenient way to communicate what 406 // the variable name is, which would make the error message even 407 // more specific. 408 if (!value.startsWith("\"")) { 409 System.out.printf( 410 "Warning: unquoted string value at %s line %d: %s%n", 411 filename, reader.getLineNumber(), value); 412 } else { 413 assert !value.endsWith("\""); 414 System.out.printf( 415 "Warning: unterminated string value at %s line %d: %s%n", 416 filename, reader.getLineNumber(), value); 417 } 418 System.out.printf( 419 "Proceeding anyway. Please report a bug in the tool that made the data trace file."); 420 } 421 value = StringsPlume.unescapeJava(value); 422 return value.intern(); 423 } else if (base == BASE_CHAR) { 424 // This will fail if the character is output as an integer 425 // (as I believe the C front end does). 426 char c; 427 if (value.length() == 1) { 428 c = value.charAt(0); 429 } else if ((value.length() == 2) && (value.charAt(0) == '\\')) { 430 c = StringsPlume.unescapeJava(value).charAt(0); 431 } else if ((value.length() == 4) && (value.charAt(0) == '\\')) { 432 Byte b = Byte.decode("0" + value.substring(1)); 433 return Intern.internedLong(b.longValue()); 434 } else { 435 throw new IllegalArgumentException("Bad character: " + value); 436 } 437 return Intern.internedLong((int) c); 438 } 439 // When parse_value is called from FileIO.read_ppt_decl, we have 440 // not set file_rep_type. Hence, rep_type is still file_rep_type 441 // and BASE_BOOLEAN is legal. 442 else if ((base == BASE_INT) || (base == BASE_BOOLEAN)) { 443 // File rep type might be int, boolean, or hashcode. 444 // If we had the declared type, we could do error-checking here. 445 // (Example: no hashcode should be negative, nor any boolean > 1.) 446 if (value.equals("nonsensical")) { 447 return null; 448 } 449 if (value.equals("false") || value.equals("0")) { 450 return LongZero; 451 } 452 if (value.equals("true") || value.equals("1")) { 453 return LongOne; 454 } 455 if (value.equals("null")) { 456 return LongZero; 457 } 458 return Intern.internedLong(myParseLong(value)); 459 } else if (base == BASE_DOUBLE) { 460 // Must ignore case, because dfej outputs "NaN", while dfec 461 // outputs "nan". dfec outputs "nan", because this string 462 // comes from the C++ library. 463 if (value.equalsIgnoreCase("NaN")) { 464 return DoubleNaN; 465 } 466 if (value.equalsIgnoreCase("Infinity") || value.equals("inf")) { 467 return DoublePositiveInfinity; 468 } 469 if (value.equalsIgnoreCase("-Infinity") || value.equals("-inf")) { 470 return DoubleNegativeInfinity; 471 } 472 return Intern.internedDouble(value); 473 } else if ((base == BASE_HASHCODE) 474 || (base == BASE_LONG) 475 || (base == BASE_LONG_LONG) 476 || (base == BASE_SHORT)) { 477 throw new Error("not a rep type: illegal base type " + base); 478 } else { 479 throw new Error("unrecognized base type " + base); 480 } 481 } 482 483 public final @Nullable @Interned Object parse_value_array_1d( 484 String value, LineNumberReader reader, String filename) { 485 // System.out.println(format() + ".parse(\"" + value + "\")"); 486 487 String value_orig = value; // we will side-effect the parameter 488 489 // variable is an array 490 assert dimensions == 1; 491 492 value = value.trim(); 493 494 // Deal with [] surrounding array output (permits 495 // distinguishing between null and an array containing just null). 496 if (value.startsWith("[") && value.endsWith("]")) { 497 value = value.substring(1, value.length() - 1).trim(); 498 } else { 499 throw new IllegalArgumentException("Array values must be enlosed in square brackets"); 500 } 501 502 // Elements of value_strings can be null only if base == BASE_STRING. 503 // [I think they can never be null. -MDE] 504 String[] value_strings; 505 if (value.length() == 0) { 506 value_strings = new String[0]; 507 } else if (base == BASE_STRING) { 508 // This properly handles strings containing embedded spaces. 509 List<@Nullable String> v = new ArrayList<>(); 510 StreamTokenizer parser = new StreamTokenizer(new StringReader(value)); 511 parser.quoteChar('\"'); 512 try { 513 while (parser.nextToken() != StreamTokenizer.TT_EOF) { 514 if (parser.ttype == '\"') { 515 v.add(parser.sval); 516 } else if (parser.ttype == StreamTokenizer.TT_WORD) { 517 assert parser.sval != null 518 : "@AssumeAssertion(nullness): dependent: representation invariant of" 519 + " StreamTokenizer"; 520 if (parser.sval.equals("nonsensical")) { 521 return null; 522 } 523 assert parser.sval.equals("null"); 524 v.add(null); 525 } else if (parser.ttype == StreamTokenizer.TT_NUMBER) { 526 v.add(Integer.toString((int) parser.nval)); 527 } else { 528 System.out.printf( 529 "Warning: at %s line %d%n" 530 + " bad ttype %c [int=%d] while parsing %s%n" 531 + " Proceeding with value 'null'%n", 532 filename, 533 reader.getLineNumber(), 534 (@Signed char) parser.ttype, 535 parser.ttype, 536 value_orig); 537 v.add(null); 538 } 539 } 540 } catch (Exception e) { 541 throw new Error(e); 542 } 543 // Avoid nullness warnings about elements of value_strings 544 @SuppressWarnings("nullness") 545 String[] value_strings_result = v.toArray(new @Nullable String[0]); 546 value_strings = value_strings_result; 547 } else { 548 value_strings = Global.ws_regexp.split(value); 549 } 550 int len = value_strings.length; 551 552 // This big if ... else should deal with all the primitive types -- 553 // or at least all the ones that can be rep_types. 554 // ("long" and "short" cannot be rep_types; for simplicity, variables 555 // declared as long or short have the "int" rep_type.) 556 if (base == BASE_INT) { 557 long[] result = new long[len]; 558 for (int i = 0; i < len; i++) { 559 if (value_strings[i].equals("nonsensical")) { 560 return null; 561 } else if (value_strings[i].equals("null")) result[i] = 0; 562 else if (value_strings[i].equals("false")) result[i] = 0; 563 else if (value_strings[i].equals("true")) result[i] = 1; 564 else { 565 result[i] = myParseLong(value_strings[i]); 566 } 567 } 568 return Intern.intern(result); 569 } else if (base == BASE_DOUBLE) { 570 double[] result = new double[len]; 571 for (int i = 0; i < len; i++) { 572 if (value_strings[i].equals("nonsensical")) { 573 return null; 574 } else if (value_strings[i].equals("null")) result[i] = 0; 575 else if (value_strings[i].equalsIgnoreCase("NaN")) result[i] = Double.NaN; 576 else if (value_strings[i].equalsIgnoreCase("Infinity") || value_strings[i].equals("inf")) { 577 result[i] = Double.POSITIVE_INFINITY; 578 } else if (value_strings[i].equalsIgnoreCase("-Infinity") 579 || value_strings[i].equals("-inf")) { 580 result[i] = Double.NEGATIVE_INFINITY; 581 } else { 582 result[i] = Double.parseDouble(value_strings[i]); 583 } 584 } 585 return Intern.intern(result); 586 } else if (base == BASE_STRING) { 587 // First, intern each String in the array ... 588 @Interned String[] value_strings_elts_interned = Intern.internStrings(value_strings); 589 // ... then, intern the entire array, and return it 590 return Intern.intern(value_strings_elts_interned); 591 } else { 592 throw new Error("Can't yet parse array of base type " + base); 593 } 594 595 // This is a more general technique; but when will we need 596 // such generality? 597 // // not elementType() because that interns; here, there is no 598 // // need to do the work of interning (I think) 599 // ProglangType elt_type = elementType(); 600 // Object[] result = new Object[len]; 601 // for (int i = 0; i<len; i++) 602 // result[i] = ***; 603 604 } 605 606 public final @Nullable @Interned Object parse_value_array_2d( 607 String value, LineNumberReader reader, String filename) { 608 // System.out.println(format() + ".parse(\"" + value + "\")"); 609 610 assert dimensions == 2; 611 if (base == BASE_CHAR) { 612 // Array of strings 613 throw new Error("To implement"); 614 // value = tuple(eval(value)); 615 } else { 616 throw new Error("Can't parse a value of type " + format()); 617 } 618 } 619 620 public boolean baseIsPrimitive() { 621 return ((base == BASE_BOOLEAN) 622 || (base == BASE_BYTE) 623 || (base == BASE_CHAR) 624 || (base == BASE_DOUBLE) 625 || (base == BASE_FLOAT) 626 || (base == BASE_INT) 627 || (base == BASE_LONG) 628 || (base == BASE_LONG_LONG) 629 || (base == BASE_SHORT)); 630 } 631 632 @Pure 633 public boolean isPrimitive() { 634 return (dimensions == 0) && baseIsPrimitive(); 635 } 636 637 // Does not include boolean. Is that intentional? (If it were added, 638 // we would need to change isIndex().) 639 public boolean baseIsIntegral() { 640 return ((base == BASE_BYTE) 641 || (base == BASE_CHAR) 642 || (base == BASE_INT) 643 || (base == BASE_LONG) 644 || (base == BASE_LONG_LONG) 645 || (base == BASE_SHORT) 646 || (base == BASE_INTEGER)); 647 } 648 649 @Pure 650 public boolean isIntegral() { 651 return (dimensions == 0) && baseIsIntegral(); 652 } 653 654 // More efficient than elementType().isIntegral() 655 public boolean elementIsIntegral() { 656 return (dimensions == 1) && baseIsIntegral(); 657 } 658 659 public boolean elementIsFloat() { 660 return (dimensions == 1) && baseIsFloat(); 661 } 662 663 public boolean elementIsString() { 664 return (dimensions == 1) && baseIsString(); 665 } 666 667 // Return true if this variable is sensible as an array index. 668 @Pure 669 public boolean isIndex() { 670 return isIntegral(); 671 } 672 673 @Pure 674 public boolean isScalar() { 675 // For reptypes, checking against INT is sufficient, rather than 676 // calling isIntegral(). 677 return isIntegral() || (this == HASHCODE) || (this == BOOLEAN); 678 } 679 680 public boolean baseIsScalar() { 681 return baseIsIntegral() || (base == BASE_BOOLEAN) || (base == BASE_HASHCODE); 682 } 683 684 public boolean baseIsBoolean() { 685 return base == BASE_BOOLEAN; 686 } 687 688 public boolean baseIsFloat() { 689 return (base == BASE_DOUBLE) || (base == BASE_FLOAT); 690 } 691 692 @Pure 693 public boolean isFloat() { 694 return (dimensions == 0) && baseIsFloat(); 695 } 696 697 /** 698 * Return true if this is java.lang.Object. 699 * 700 * @return true if this is java.lang.Object 701 */ 702 @Pure 703 public boolean isObject() { 704 return (dimensions == 0) && baseIsObject(); 705 } 706 707 /** 708 * Return true if the base (the final element type) is a reference type rather than integer, 709 * float, or boolean. 710 * 711 * @return true if the base is Object 712 */ 713 public boolean baseIsObject() { 714 return !baseIsIntegral() && !baseIsFloat() && !(base == BASE_BOOLEAN); 715 } 716 717 public boolean baseIsString() { 718 return base == BASE_STRING; 719 } 720 721 @Pure 722 public boolean isString() { 723 return (dimensions == 0) && baseIsString(); 724 } 725 726 public boolean baseIsHashcode() { 727 return (base == BASE_HASHCODE); 728 } 729 730 @Pure 731 public boolean isHashcode() { 732 return (dimensions == 0) && baseIsHashcode(); 733 } 734 735 /** Does this type represent a pointer? Should only be applied to file_rep types. */ 736 @Pure 737 public boolean isPointerFileRep() { 738 return base == BASE_HASHCODE; 739 } 740 741 /** 742 * Return true if these two types can be sensibly compared to one another, or if one can be cast 743 * to the other. For instance, int is castable to long, but boolean is not castable to float, and 744 * int is not castable to int[]. This is a reflexive relationship, but not a transitive one 745 * because it might not be true for two children of a superclass, even though it's true for the 746 * superclass. 747 */ 748 public boolean comparableOrSuperclassEitherWay(ProglangType other) { 749 if (this == other) { // ProglangType objects are interned 750 return true; 751 } 752 if (this.dimensions != other.dimensions) { 753 return false; 754 } 755 boolean thisIntegral = this.baseIsIntegral(); 756 boolean otherIntegral = other.baseIsIntegral(); 757 if (thisIntegral && otherIntegral) { 758 return true; 759 } 760 // Make Object castable to everything, except booleans 761 if (((this.base == BASE_OBJECT) && other.baseIsObject()) // interned strings 762 || ((other.base == BASE_OBJECT) && baseIsObject())) { // interned strings 763 return true; 764 } 765 766 return false; 767 } 768 769 /** 770 * Return true if these two types can be sensibly compared to one another, and if non-integral, 771 * whether this could be a superclass of other. A List is comparableOrSuperclassOf to a ArrayList, 772 * but not the other way around. This is a transitive method, but not reflexive. 773 */ 774 public boolean comparableOrSuperclassOf(ProglangType other) { 775 if (this == other) // ProglangType objects are interned 776 return true; 777 if (this.dimensions != other.dimensions) { 778 return false; 779 } 780 boolean thisIntegral = this.baseIsIntegral(); 781 boolean otherIntegral = other.baseIsIntegral(); 782 if (thisIntegral && otherIntegral) { 783 return true; 784 } 785 // Make Object castable to everything, except booleans 786 if ((this.base == BASE_OBJECT) && other.baseIsObject()) // interned strings 787 return true; 788 789 return false; 790 } 791 792 // For Java programs, a @BinaryName. 793 @SideEffectFree 794 public String format(@GuardSatisfied ProglangType this) { 795 if (dimensions == 0) { 796 return base; 797 } 798 799 StringBuilder sb = new StringBuilder(); 800 sb.append(base); 801 for (int i = 0; i < dimensions; i++) { 802 sb.append("[]"); 803 } 804 return sb.toString(); 805 } 806 807 // For Java programs, a @BinaryName. 808 @SideEffectFree 809 @Override 810 public String toString(@GuardSatisfied ProglangType this) { 811 return format(); 812 } 813 814 /** 815 * Returns whether or not this declared type is a function pointer Only valid if the front end 816 * marks the function pointer with the name '*func'. 817 */ 818 @Pure 819 public boolean is_function_pointer() { 820 assert base == base.intern(); 821 return base == "*func"; // interned 822 } 823}