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