1package annotations.io; 2 3/*>>> 4import org.checkerframework.checker.nullness.qual.*; 5*/ 6 7import static java.io.StreamTokenizer.TT_EOF; 8import static java.io.StreamTokenizer.TT_NUMBER; 9import static java.io.StreamTokenizer.TT_WORD; 10 11import java.io.FileReader; 12import java.io.IOException; 13import java.io.LineNumberReader; 14import java.io.Reader; 15import java.io.StreamTokenizer; 16import java.io.StringReader; 17import java.text.Collator; 18import java.util.ArrayList; 19import java.util.Arrays; 20import java.util.Collections; 21import java.util.HashMap; 22import java.util.LinkedHashMap; 23import java.util.LinkedHashSet; 24import java.util.Map; 25import java.util.Set; 26import java.util.TreeSet; 27import java.util.regex.Pattern; 28 29import com.sun.source.tree.Tree.Kind; 30import com.sun.tools.javac.code.TypeAnnotationPosition; 31 32import annotations.Annotation; 33import annotations.AnnotationBuilder; 34import annotations.AnnotationFactory; 35import annotations.Annotations; 36import annotations.ArrayBuilder; 37import annotations.el.ABlock; 38import annotations.el.AClass; 39import annotations.el.ADeclaration; 40import annotations.el.AElement; 41import annotations.el.AExpression; 42import annotations.el.AField; 43import annotations.el.AMethod; 44import annotations.el.AScene; 45import annotations.el.ATypeElement; 46import annotations.el.ATypeElementWithType; 47import annotations.el.AnnotationDef; 48import annotations.el.BoundLocation; 49import annotations.el.InnerTypeLocation; 50import annotations.el.LocalLocation; 51import annotations.el.RelativeLocation; 52import annotations.el.TypeIndexLocation; 53import annotations.field.AnnotationAFT; 54import annotations.field.AnnotationFieldType; 55import annotations.field.ArrayAFT; 56import annotations.field.BasicAFT; 57import annotations.field.ClassTokenAFT; 58import annotations.field.EnumAFT; 59import annotations.field.ScalarAFT; 60import annotations.util.coll.VivifyingMap; 61 62import plume.ArraysMDE; 63import plume.FileIOException; 64import plume.Pair; 65 66import type.ArrayType; 67import type.BoundedType; 68import type.BoundedType.BoundKind; 69import type.DeclaredType; 70import type.Type; 71 72/** 73 * IndexFileParser provides static methods 74 * {@link #parse(LineNumberReader, AScene)}, 75 * {@link #parseFile(String, AScene)}, and 76 * {@link #parseString(String, AScene)}. 77 * Each of these parses an index file into a {@link AScene}. 78 * <p> 79 * 80 * If there are any problems, it throws a ParseException internally, or a 81 * FileIOException externally. 82 */ 83public final class IndexFileParser { 84 85 private static final String[] typeSelectors = { "bound", "identifier", 86 "type", "typeAlternative", "typeArgument", "typeParameter", 87 "underlyingType" }; 88 89 private static boolean abbreviate = true; 90 91 // The input 92 private final StreamTokenizer st; 93 94 // The output 95 private final AScene scene; 96 97 private String curPkgPrefix; 98 99 /** 100 * Holds definitions we've seen so far. Maps from annotation name to 101 * the definition itself. Maps from both the qualified name and the 102 * unqualified name. If the unqualified name is not unique, it maps 103 * to null and the qualified name should be used instead. */ 104 private final HashMap<String, AnnotationDef> defs; 105 106 public static void setAbbreviate(boolean b) { 107 abbreviate = b; 108 } 109 110 private int expectNonNegative(int i) throws ParseException { 111 if (i >= 0) { 112 return i; 113 } else { 114 throw new ParseException("Expected a nonnegative integer, got " + st); 115 } 116 } 117 118 /** True if the next thing from st is the given character. */ 119 private boolean checkChar(char c) { 120 return st.ttype == c; 121 } 122 123 /** True if the next thing from st is the given string token. */ 124 private boolean checkKeyword(String s) { 125 return st.ttype == TT_WORD && st.sval.equals(s); 126 } 127 128 /** 129 * Return true if the next thing to be read from st is the given string. 130 * In that case, also read past the given string. 131 * If the result is false, reads nothing from st. 132 */ 133 private boolean matchChar(char c) throws IOException { 134 if (checkChar(c)) { 135 st.nextToken(); 136 return true; 137 } else { 138 return false; 139 } 140 } 141 142 /** 143 * Return true if the next thing to be read from st is the given string. 144 * In that case, also read past the given string. 145 * If the result is false, reads nothing from st. 146 */ 147 private boolean matchKeyword(String s) throws IOException { 148 if (checkKeyword(s)) { 149 st.nextToken(); 150 return true; 151 } else { 152 return false; 153 } 154 } 155 156 /** Reads from st. If the result is not c, throws an exception. */ 157 private void expectChar(char c) throws IOException, ParseException { 158 if (! matchChar(c)) { 159 // Alternately, could use st.toString(). 160 String found; 161 switch (st.ttype) { 162 case StreamTokenizer.TT_WORD: found = st.sval; break; 163 case StreamTokenizer.TT_NUMBER: found = "" + st.nval; break; 164 case StreamTokenizer.TT_EOL: found = "end of line"; break; 165 case StreamTokenizer.TT_EOF: found = "end of file"; break; 166 default: found = "'" + ((char) st.ttype) + "'"; break; 167 } 168 throw new ParseException("Expected '" + c + "', found " + found); 169 } 170 } 171 172 /** Reads from st. If the result is not s, throws an exception. */ 173 private void expectKeyword(String s) throws IOException, 174 ParseException { 175 if (! matchKeyword(s)) { 176 throw new ParseException("Expected `" + s + "'"); 177 } 178 } 179 180 private static final Set<String> knownKeywords; 181 static { 182 String[] knownKeywords_array = 183 { "abstract", "assert", "boolean", "break", "byte", "case", 184 "catch", "char", "class", "const", "continue", 185 "default", "do", "double", "else", "enum", "extends", 186 "false", "final", "finally", "float", "for", "if", 187 "goto", "implements", "import", "instanceof", "int", 188 "interface", "long", "native", "new", "null", 189 "package", "private", "protected", "public", "return", 190 "short", "static", "strictfp", "super", "switch", 191 "synchronized", "this", "throw", "throws", "transient", 192 "true", "try", "void", "volatile", "while", }; 193 knownKeywords = new LinkedHashSet<String>(); 194 Collections.addAll(knownKeywords, knownKeywords_array); 195 } 196 197 private boolean isValidIdentifier(String x) { 198 if (x.length() == 0 || !Character.isJavaIdentifierStart(x.charAt(0)) 199 || knownKeywords.contains(x)) 200 return false; 201 for (int i = 1; i < x.length(); i++) { 202 if (!Character.isJavaIdentifierPart(x.charAt(i))) { 203 return false; 204 } 205 } 206 return true; 207 } 208 209 private String checkIdentifier() { 210 if (st.sval == null) { 211 return null; 212 } else { 213 String val = st.sval; 214 if (st.ttype == TT_WORD && isValidIdentifier(val)) { 215 return st.sval; 216 } else { 217 return null; 218 } 219 } 220 } 221 222 private String matchIdentifier() throws IOException { 223 String x = checkIdentifier(); 224 if (x != null) { 225 st.nextToken(); 226 return x; 227 } else { 228 return null; 229 } 230 } 231 232 private String expectIdentifier() throws IOException, ParseException { 233 String id = matchIdentifier(); 234 if (id == null) { throw new ParseException("Expected an identifier"); } 235 return id; 236 } 237 238 private String checkPrimitiveType() { 239 if (st.sval == null) { 240 return null; 241 } else { 242 String val = st.sval; 243 if (st.ttype == TT_WORD && primitiveTypes.containsKey(val)) { 244 return st.sval; 245 } else { 246 return null; 247 } 248 } 249 } 250 251 private String matchPrimitiveType() throws IOException { 252 String x = checkPrimitiveType(); 253 if (x != null) { 254 st.nextToken(); 255 return x; 256 } else { 257 return null; 258 } 259 } 260 261 // an identifier, or a sequence of dot-separated identifiers 262 private String expectQualifiedName() throws IOException, ParseException { 263 String name = expectIdentifier(); 264 while (matchChar('.')) { 265 name += '.' + expectIdentifier(); 266 } 267 return name; 268 } 269 270 private int checkNNInteger() { 271 if (st.ttype == TT_NUMBER) { 272 int x = (int) st.nval; 273 if (x == st.nval && x >= -1) // shouldn't give us a huge number 274 return x; 275 } 276 return -1; 277 } 278 279 private int matchNNInteger() throws IOException { 280 int x = checkNNInteger(); 281 if (x >= -1) { 282 st.nextToken(); 283 return x; 284 } else { 285 return -1; 286 } 287 } 288 289 // Mapping from primitive types and void to their corresponding 290 // class objects. Class.forName doesn't directly support these. 291 // Using this map we can go from "void.class" to the correct 292 // Class object. 293 private static final Map<String, Class<?>> primitiveTypes; 294 static { 295 Map<String, Class<?>> pt = new LinkedHashMap<String, Class<?>>(); 296 pt.put("byte", byte.class); 297 pt.put("short", short.class); 298 pt.put("int", int.class); 299 pt.put("long", long.class); 300 pt.put("float", float.class); 301 pt.put("double", double.class); 302 pt.put("char", char.class); 303 pt.put("boolean", boolean.class); 304 pt.put("void", void.class); 305 primitiveTypes = pt; 306 } 307 308 /** Parse scalar annotation value. */ 309 // HMMM can a (readonly) Integer be casted to a writable Object? 310 private Object parseScalarAFV(ScalarAFT aft) throws IOException, ParseException { 311 if (aft instanceof BasicAFT) { 312 Object val; 313 BasicAFT baft = (BasicAFT) aft; 314 Class<?> type = baft.type; 315 if (type == boolean.class) { 316 if (matchKeyword("true")) { 317 val = true; 318 } else if (matchKeyword("false")) { 319 val = false; 320 } else { 321 throw new ParseException("Expected `true' or `false'"); 322 } 323 } else if (type == char.class) { 324 if (st.ttype == '\'' && st.sval.length() == 1) { 325 val = st.sval.charAt(0); 326 } else { 327 throw new ParseException("Expected a character literal"); 328 } 329 st.nextToken(); 330 } else if (type == String.class) { 331 if (st.ttype == '"') { 332 val = st.sval; 333 } else { 334 throw new ParseException("Expected a string literal"); 335 } 336 st.nextToken(); 337 } else { 338 if (st.ttype == TT_NUMBER) { 339 double n = st.nval; 340 // TODO validate the literal better 341 // HMMM StreamTokenizer can't handle all floating point 342 // numbers; in particular, scientific notation is a problem 343 if (type == byte.class) { 344 val = (byte) n; 345 } else if (type == short.class) { 346 val = (short) n; 347 } else if (type == int.class) { 348 val = (int) n; 349 } else if (type == long.class) { 350 val = (long) n; 351 } else if (type == float.class) { 352 val = (float) n; 353 } else if (type == double.class) { 354 val = n; 355 } else { 356 throw new AssertionError(); 357 } 358 st.nextToken(); 359 } else { 360 throw new ParseException( 361 "Expected a number literal"); 362 } 363 } 364 assert aft.isValidValue(val); 365 return val; 366 } else if (aft instanceof ClassTokenAFT) { 367 // Expect the class name in the format that Class.forName accepts, 368 // which is some very strange format. 369 // Example inputs followed by their Java source ".class" equivalent: 370 // [[I.class for int[][].class 371 // [java.util.Map for Map[].class 372 // java.util.Map for Map.class 373 // Have to use fully-qualified names, i.e. "Object" alone won't work. 374 // Also note use of primitiveTypes map for primitives and void. 375 int arrays = 0; 376 StringBuilder type = new StringBuilder(); 377 while (matchChar('[')) { 378 // Array dimensions as prefix 379 ++arrays; 380 } 381 while (!matchKeyword("class")) { 382 if (st.ttype >= 0) { 383 type.append((char) st.ttype); 384 } else if (st.ttype == TT_WORD) { 385 type.append(st.sval); 386 } else { 387 throw new ParseException("Found something that doesn't belong in a signature"); 388 } 389 st.nextToken(); 390 } 391 392 // Drop the '.' before the "class" 393 type.deleteCharAt(type.length()-1); 394 // expectKeyword("class"); 395 396 // Add arrays as prefix in the type. 397 while (arrays-->0) { 398 type.insert(0, '['); 399 } 400 401 try { 402 String sttype = type.toString(); 403 Class<?> tktype; 404 if (primitiveTypes.containsKey(sttype)) { 405 tktype = primitiveTypes.get(sttype); 406 } else { 407 tktype = Class.forName(sttype); 408 } 409 assert aft.isValidValue(tktype); 410 return tktype; 411 } catch (ClassNotFoundException e) { 412 throw new ParseException("Could not load class: " + type, e); 413 } 414 } else if (aft instanceof EnumAFT) { 415 String name = expectQualifiedName(); 416 assert aft.isValidValue(name); 417 return name; 418 } else if (aft instanceof AnnotationAFT) { 419 AnnotationAFT aaft = (AnnotationAFT) aft; 420 AnnotationDef d = parseAnnotationHead(); 421 if (! d.name.equals(aaft.annotationDef.name)) { 422 throw new ParseException("Got an " + d.name 423 + " subannotation where an " + aaft.annotationDef.name 424 + " was expected"); 425 } 426 AnnotationBuilder ab = AnnotationFactory.saf.beginAnnotation(d); 427 // interested in this annotation, 428 // so should be interested in subannotations 429 assert ab != null; 430 AnnotationBuilder ab2 = (AnnotationBuilder) ab; 431 Annotation suba = parseAnnotationBody(d, ab2); 432 assert aft.isValidValue(suba); 433 return suba; 434 } else { 435 throw new AssertionError("IndexFileParser.parseScalarAFV: unreachable code."); 436 } 437 } 438 439 private void parseAndAddArrayAFV(ArrayAFT aaft, ArrayBuilder arrb) throws IOException, ParseException { 440 ScalarAFT comp; 441 if (aaft.elementType != null) { 442 comp = aaft.elementType; 443 } else { 444 throw new IllegalArgumentException("array AFT has null elementType"); 445 } 446 if (matchChar('{')) { 447 // read an array 448 while (!matchChar('}')) { 449 arrb.appendElement(parseScalarAFV(comp)); 450 if (!checkChar('}')) { 451 expectChar(','); 452 } 453 } 454 } else { 455 // not an array, so try reading just one value as an array 456 arrb.appendElement(parseScalarAFV(comp)); 457 } 458 arrb.finish(); 459 } 460 461 // parses a field such as "f1=5" in "@A(f1=5, f2=10)". 462 private void parseAnnotationField(AnnotationDef d, AnnotationBuilder ab) throws IOException, ParseException { 463 String fieldName; 464 if (d.fieldTypes.size() == 1 465 && d.fieldTypes.containsKey("value")) { 466 fieldName = "value"; 467 if (matchKeyword("value")) { 468 expectChar('='); 469 } 470 } else { 471 fieldName = expectIdentifier(); 472 expectChar('='); 473 } 474 // HMMM let's hope the builder checks for duplicate fields 475 // because we can't do it any more 476 AnnotationFieldType aft1 = d.fieldTypes.get(fieldName); 477 if (aft1 == null) { 478 throw new ParseException("The annotation type " + d.name 479 + " has no field called " + fieldName); 480 } 481 AnnotationFieldType aft = (AnnotationFieldType) aft1; 482 if (aft instanceof ArrayAFT) { 483 ArrayAFT aaft = (ArrayAFT) aft; 484 if (aaft.elementType == null) { 485 // Array of unknown element type--must be zero-length 486 expectChar('{'); 487 expectChar('}'); 488 ab.addEmptyArrayField(fieldName); 489 } else { 490 parseAndAddArrayAFV(aaft, ab.beginArrayField(fieldName, aaft)); 491 } 492 } else if (aft instanceof ScalarAFT) { 493 ScalarAFT saft = (ScalarAFT) aft; 494 Object value = parseScalarAFV(saft); 495 ab.addScalarField(fieldName, saft, value); 496 } else { 497 throw new AssertionError(); 498 } 499 } 500 501 // reads the "@A" part of an annotation such as "@A(f1=5, f2=10)". 502 private AnnotationDef parseAnnotationHead() throws IOException, 503 ParseException { 504 expectChar('@'); 505 String name = expectQualifiedName(); 506 AnnotationDef d = defs.get(name); 507 if (d == null) { 508 // System.err.println("No definition for annotation type " + name); 509 // System.err.printf(" defs contains %d entries%n", defs.size()); 510 // for (Map.Entry<String,AnnotationDef> entry : defs.entrySet()) { 511 // System.err.printf(" defs entry: %s => %s%n", entry.getKey(), entry.getValue()); 512 // } 513 throw new ParseException("No definition for annotation type " + name); 514 } 515 return d; 516 } 517 518 private Annotation parseAnnotationBody(AnnotationDef d, AnnotationBuilder ab) throws IOException, ParseException { 519 if (matchChar('(')) { 520 parseAnnotationField(d, ab); 521 while (matchChar(',')) { 522 parseAnnotationField(d, ab); 523 } 524 expectChar(')'); 525 } 526 Annotation ann = ab.finish(); 527 if (! ann.def.equals(d)) { 528 throw new ParseException( 529 "parseAnnotationBody: Annotation def isn't as it should be.\n" + d + "\n" + ann.def); 530 } 531 if (ann.def().fieldTypes.size() != d.fieldTypes.size()) { 532 throw new ParseException( 533 "At least one annotation field is missing"); 534 } 535 return ann; 536 } 537 538 private void parseAnnotations(AElement e) 539 throws IOException, ParseException { 540 while (checkChar('@')) { 541 AnnotationDef d = parseAnnotationHead(); 542 AnnotationBuilder ab = AnnotationFactory.saf.beginAnnotation(d); 543 if (ab == null) { 544 // don't care about the result 545 // but need to skip over it anyway 546 @SuppressWarnings("unused") 547 Object trash = parseAnnotationBody(d, AnnotationFactory.saf 548 .beginAnnotation(d)); 549 } else { 550 Annotation a = parseAnnotationBody(d, ab); 551 for (Annotation other : e.tlAnnotationsHere) { 552 if (a.def.name.equals(other.def.name)) { 553 System.err.println( 554 "WARNING: duplicate annotation of type " 555 + a.def().name); 556 continue; 557 } 558 } 559 Annotation tla = a; 560 if (! tla.def.equals(d)) { 561 throw new ParseException("Bad def"); 562 } 563 e.tlAnnotationsHere.add(tla); 564 } 565 } 566 } 567 568 private ScalarAFT parseScalarAFT() throws IOException, ParseException { 569 for (BasicAFT baft : BasicAFT.bafts.values()) { 570 if (matchKeyword(baft.toString())) { 571 return baft; 572 } 573 } 574 // wasn't a BasicAFT 575 if (matchKeyword("Class")) { 576 return ClassTokenAFT.ctaft/* dumpParameterization() */; 577 } else if (matchKeyword("enum")) { 578 String name = expectQualifiedName(); 579 if (abbreviate) { 580 int i = name.lastIndexOf('.'); 581 if (i >= 0) { 582 String baseName = name.substring(i+1); 583 Set<String> set1 = scene.imports.get(name); 584 Set<String> set2 = scene.imports.get(baseName); 585 if (set1 == null) { 586 set1 = new TreeSet<String>(); 587 scene.imports.put(name, set1); 588 } 589 if (set2 == null) { 590 set2 = new TreeSet<String>(); 591 scene.imports.put(name, set2); 592 } 593 set1.add(name); 594 set2.add(name); 595 name = baseName; 596 } 597 } 598 return new EnumAFT(name); 599 } else if (matchKeyword("annotation-field")) { 600 String name = expectQualifiedName(); 601 AnnotationDef ad = defs.get(name); 602 if (ad == null) { 603 throw new ParseException("Annotation type " + name + " used as a field before it is defined"); 604 } 605 return new AnnotationAFT((AnnotationDef) ad); 606 } else { 607 throw new ParseException( 608 "Expected the beginning of an annotation field type: " 609 + "a primitive type, `String', `Class', `enum', or `annotation-field'. Got '" 610 + st.sval + "'."); 611 } 612 } 613 614 private AnnotationFieldType parseAFT() throws IOException, 615 ParseException { 616 if (matchKeyword("unknown")) { 617 // Handle unknown[]; see AnnotationBuilder#addEmptyArrayField 618 expectChar('['); 619 expectChar(']'); 620 return new ArrayAFT(null); 621 } 622 ScalarAFT baseAFT = parseScalarAFT(); 623 // only one level of array is permitted 624 if (matchChar('[')) { 625 expectChar(']'); 626 return new ArrayAFT(baseAFT); 627 } else { 628 return baseAFT; 629 } 630 } 631 632 private void parseAnnotationDef() throws IOException, ParseException { 633 expectKeyword("annotation"); 634 635 expectChar('@'); 636 String basename = expectIdentifier(); 637 String fullName = curPkgPrefix + basename; 638 639 AnnotationDef ad = new AnnotationDef(fullName); 640 expectChar(':'); 641 parseAnnotations(ad); 642 643 Map<String, AnnotationFieldType> fields = 644 new LinkedHashMap<String, AnnotationFieldType>(); 645 646 // yuck; it would be nicer to do a positive match 647 while (st.ttype != TT_EOF && !checkKeyword("annotation") 648 && !checkKeyword("class") && !checkKeyword("package")) { 649 AnnotationFieldType type = parseAFT(); 650 String name = expectIdentifier(); 651 if (fields.containsKey(name)) { 652 throw new ParseException("Duplicate definition of field " 653 + name); 654 } 655 fields.put(name, type); 656 } 657 658 ad.setFieldTypes(fields); 659 660 // Now add the definition to the map of all definitions. 661 addDef(ad, basename); 662 663 } 664 665 // Add the definition to the map of all definitions. 666 // also see addDef(AnnotationDef, String). 667 public void addDef(AnnotationDef ad) throws ParseException { 668 String basename = ad.name; 669 int dotPos = basename.lastIndexOf('.'); 670 if (dotPos != -1) { 671 basename = basename.substring(dotPos + 1); 672 } 673 addDef(ad, basename); 674 } 675 676 // Add the definition to the map of all definitions. 677 public void addDef(AnnotationDef ad, String basename) throws ParseException { 678 // System.out.println("addDef:" + ad); 679 680 if (defs.containsKey(ad.name)) { 681 // TODO: permit identical re-definition 682 System.err.println("Duplicate definition of annotation type " + ad.name); 683 } 684 defs.put(ad.name, ad); 685 // Add short name; but if it's already there, remove it to avoid ambiguity. 686 if (! basename.equals(ad.name)) { 687 if (defs.containsKey(basename)) { 688 // not "defs.remove(basename)" because then a subsequent 689 // one could get added, which would be wrong. 690 defs.put(basename, null); 691 } else { 692 defs.put(basename, ad); 693 } 694 } 695 } 696 697 698 private void parseInnerTypes(ATypeElement e) 699 throws IOException, ParseException { 700 parseInnerTypes(e, 0); 701 } 702 703 private void parseInnerTypes(ATypeElement e, int offset) 704 throws IOException, ParseException { 705 while (matchKeyword("inner-type")) { 706 ArrayList<Integer> locNumbers = 707 new ArrayList<Integer>(); 708 locNumbers.add(offset + expectNonNegative(matchNNInteger())); 709 // TODO: currently, we simply read the binary representation. 710 // Should we read a higher-level format? 711 while (matchChar(',')) { 712 locNumbers.add(expectNonNegative(matchNNInteger())); 713 } 714 InnerTypeLocation loc; 715 try { 716 loc = new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(locNumbers)); 717 } catch (AssertionError ex) { 718 throw new ParseException(ex.getMessage(), ex); 719 } 720 AElement it = e.innerTypes.vivify(loc); 721 expectChar(':'); 722 parseAnnotations(it); 723 } 724 } 725 726 private void parseBounds(VivifyingMap<BoundLocation, ATypeElement> bounds) 727 throws IOException, ParseException { 728 while (checkKeyword("typeparam") || checkKeyword("bound")) { 729 if (matchKeyword("typeparam")) { 730 int paramIndex = expectNonNegative(matchNNInteger()); 731 BoundLocation bl = new BoundLocation(paramIndex, -1); 732 ATypeElement b = bounds.vivify(bl); 733 expectChar(':'); 734 parseAnnotations(b); 735 // does this make sense? 736 parseInnerTypes(b); 737 } else if (matchKeyword("bound")) { 738 // expectChar(','); 739 int paramIndex = expectNonNegative(matchNNInteger()); 740 expectChar('&'); 741 int boundIndex = expectNonNegative(matchNNInteger()); 742 BoundLocation bl = new BoundLocation(paramIndex, boundIndex); 743 ATypeElement b = bounds.vivify(bl); 744 expectChar(':'); 745 parseAnnotations(b); 746 // does this make sense? 747 parseInnerTypes(b); 748 } else { 749 throw new Error("impossible"); 750 } 751 } 752 } 753 754 private void parseExtends(AClass cls) throws IOException, ParseException { 755 expectKeyword("extends"); 756 TypeIndexLocation idx = new TypeIndexLocation(-1); 757 ATypeElement ext = cls.extendsImplements.vivify(idx); 758 expectChar(':'); 759 parseAnnotations(ext); 760 parseInnerTypes(ext); 761 } 762 763 private void parseImplements(AClass cls) throws IOException, ParseException { 764 expectKeyword("implements"); 765 int implIndex = expectNonNegative(matchNNInteger()); 766 TypeIndexLocation idx = new TypeIndexLocation(implIndex); 767 ATypeElement impl = cls.extendsImplements.vivify(idx); 768 expectChar(':'); 769 parseAnnotations(impl); 770 parseInnerTypes(impl); 771 } 772 773 private void parseField(AClass c) throws IOException, 774 ParseException { 775 expectKeyword("field"); 776 String name = expectIdentifier(); 777 AField f = c.fields.vivify(name); 778 779 expectChar(':'); 780 parseAnnotations(f); 781 if (checkKeyword("type") && matchKeyword("type")) { 782 expectChar(':'); 783 parseAnnotations(f.type); 784 parseInnerTypes(f.type); 785 } 786 787 f.init = c.fieldInits.vivify(name); 788 parseExpression(f.init); 789 parseASTInsertions(f); 790 } 791 792 private void parseStaticInit(AClass c) throws IOException, 793 ParseException { 794 expectKeyword("staticinit"); 795 expectChar('*'); 796 int blockIndex = expectNonNegative(matchNNInteger()); 797 expectChar(':'); 798 799 ABlock staticinit = c.staticInits.vivify(blockIndex); 800 parseBlock(staticinit); 801 } 802 803 private void parseInstanceInit(AClass c) throws IOException, 804 ParseException { 805 expectKeyword("instanceinit"); 806 expectChar('*'); 807 int blockIndex = expectNonNegative(matchNNInteger()); 808 expectChar(':'); 809 810 ABlock instanceinit = c.instanceInits.vivify(blockIndex); 811 parseBlock(instanceinit); 812 } 813 814 private void parseMethod(AClass c) throws IOException, 815 ParseException { 816 expectKeyword("method"); 817 // special case: method could be <init> or <clinit> 818 String key; 819 if (matchChar('<')) { 820 String basename = expectIdentifier(); 821 if (!(basename.equals("init") || basename.equals("clinit"))) { 822 throw new ParseException( 823 "The only special methods allowed are <init> and <clinit>"); 824 } 825 expectChar('>'); 826 key = "<" + basename + ">"; 827 } else { 828 key = expectIdentifier(); 829 // too bad className is private in AClass and thus must be 830 // extracted from what toString() returns 831 if (Pattern.matches("AClass: (?:[^. ]+\\.)*" + key, 832 c.toString())) { // ugh 833 key = "<init>"; 834 } 835 } 836 837 expectChar('('); 838 key += '('; 839 while (!matchChar(':')) { 840 if (st.ttype >= 0) { 841 key += st.ttype == 46 ? '/' :(char) st.ttype; 842 } else if (st.ttype == TT_WORD) { 843 key += st.sval; 844 } else { 845 throw new ParseException("Found something that doesn't belong in a signature"); 846 } 847 st.nextToken(); 848 } 849 850 AMethod m = c.methods.vivify(key); 851 parseAnnotations(m); 852 parseMethod(m); 853 } 854 855 private void parseMethod(AMethod m) throws IOException, ParseException { 856 parseBounds(m.bounds); 857 858 // Permit return value, receiver, and parameters in any order. 859 while (checkKeyword("return") || checkKeyword("receiver") || checkKeyword("parameter")) { 860 if (matchKeyword("return")) { 861 expectChar(':'); 862 parseAnnotations(m.returnType); 863 parseInnerTypes(m.returnType); 864 } else if (matchKeyword("parameter")) { 865 // make "#" optional 866 if (checkChar('#')) { 867 matchChar('#'); 868 } 869 int idx = expectNonNegative(matchNNInteger()); 870 AField p = m.parameters.vivify(idx); 871 expectChar(':'); 872 parseAnnotations(p); 873 if (checkKeyword("type") && matchKeyword("type")) { 874 expectChar(':'); 875 parseAnnotations(p.type); 876 parseInnerTypes(p.type); 877 } 878 } else if (matchKeyword("receiver")) { 879 expectChar(':'); 880 parseAnnotations(m.receiver.type); 881 parseInnerTypes(m.receiver.type); 882 } else { 883 throw new Error("This can't happen"); 884 } 885 } 886 887 parseBlock(m.body); 888 parseASTInsertions(m); 889 } 890 891 private void parseLambda(AMethod m) throws IOException, ParseException { 892 while (checkKeyword("parameter")) { 893 matchKeyword("parameter"); 894 // make "#" optional 895 if (checkChar('#')) { 896 matchChar('#'); 897 } 898 int idx = expectNonNegative(matchNNInteger()); 899 AField p = m.parameters.vivify(idx); 900 expectChar(':'); 901 parseAnnotations(p); 902 if (checkKeyword("type") && matchKeyword("type")) { 903 expectChar(':'); 904 parseAnnotations(p.type); 905 parseInnerTypes(p.type); 906 } 907 } 908 909 // parseBlock(m.body, true); 910 parseASTInsertions(m); 911 } 912 913 private void parseBlock(ABlock bl) throws IOException, 914 ParseException { 915 boolean matched = true; 916 917 while (matched) { 918 matched = false; 919 920 while (checkKeyword("local")) { 921 matchKeyword("local"); 922 matched = true; 923 LocalLocation loc; 924 if (checkNNInteger() != -1) { 925 // the local variable is specified by bytecode index/range 926 int index = expectNonNegative(matchNNInteger()); 927 expectChar('#'); 928 int scopeStart = expectNonNegative(matchNNInteger()); 929 expectChar('+'); 930 int scopeLength = expectNonNegative(matchNNInteger()); 931 loc = new LocalLocation(index, scopeStart, scopeLength); 932 } else { 933 // look for a valid identifier for the local variable 934 String lvar = expectIdentifier(); 935 int varIndex; 936 if (checkChar('*')) { 937 expectChar('*'); 938 varIndex = expectNonNegative(matchNNInteger()); 939 } else { 940 // default the variable index to 0, the most common case 941 varIndex = 0; 942 } 943 loc = new LocalLocation(lvar, varIndex); 944 } 945 AField l = bl.locals.vivify(loc); 946 expectChar(':'); 947 parseAnnotations(l); 948 if (checkKeyword("type") && matchKeyword("type")) { 949 expectChar(':'); 950 parseAnnotations(l.type); 951 parseInnerTypes(l.type); 952 } 953 } 954 matched = parseExpression(bl) || matched; 955 } 956 } 957 958 private boolean parseExpression(AExpression exp) throws IOException, 959 ParseException { 960 boolean matched = true; 961 boolean evermatched = false; 962 963 while (matched) { 964 matched = false; 965 966 while (checkKeyword("typecast")) { 967 matchKeyword("typecast"); 968 matched = true; 969 evermatched = true; 970 RelativeLocation loc; 971 if (checkChar('#')) { 972 expectChar('#'); 973 int offset = expectNonNegative(matchNNInteger()); 974 int type_index = 0; 975 if (checkChar(',')) { 976 expectChar(','); 977 type_index = expectNonNegative(matchNNInteger()); 978 } 979 loc = RelativeLocation.createOffset(offset, type_index); 980 } else { 981 expectChar('*'); 982 int index = expectNonNegative(matchNNInteger()); 983 int type_index = 0; 984 if (checkChar(',')) { 985 expectChar(','); 986 type_index = expectNonNegative(matchNNInteger()); 987 } 988 loc = RelativeLocation.createIndex(index, type_index); 989 } 990 ATypeElement t = exp.typecasts.vivify(loc); 991 expectChar(':'); 992 parseAnnotations(t); 993 parseInnerTypes(t); 994 } 995 while (checkKeyword("instanceof")) { 996 matchKeyword("instanceof"); 997 matched = true; 998 evermatched = true; 999 RelativeLocation loc; 1000 if (checkChar('#')) { 1001 expectChar('#'); 1002 int offset = expectNonNegative(matchNNInteger()); 1003 loc = RelativeLocation.createOffset(offset, 0); 1004 } else { 1005 expectChar('*'); 1006 int index = expectNonNegative(matchNNInteger()); 1007 loc = RelativeLocation.createIndex(index, 0); 1008 } 1009 ATypeElement i = exp.instanceofs.vivify(loc); 1010 expectChar(':'); 1011 parseAnnotations(i); 1012 parseInnerTypes(i); 1013 } 1014 while (checkKeyword("new")) { 1015 matchKeyword("new"); 1016 matched = true; 1017 evermatched = true; 1018 RelativeLocation loc; 1019 if (checkChar('#')) { 1020 expectChar('#'); 1021 int offset = expectNonNegative(matchNNInteger()); 1022 loc = RelativeLocation.createOffset(offset, 0); 1023 } else { 1024 expectChar('*'); 1025 int index = expectNonNegative(matchNNInteger()); 1026 loc = RelativeLocation.createIndex(index, 0); 1027 } 1028 ATypeElement n = exp.news.vivify(loc); 1029 expectChar(':'); 1030 parseAnnotations(n); 1031 parseInnerTypes(n); 1032 } 1033 while (checkKeyword("call")) { 1034 matchKeyword("call"); 1035 matched = true; 1036 evermatched = true; 1037 int i; 1038 boolean isOffset = checkChar('#'); 1039 expectChar(isOffset ? '#' : '*'); 1040 i = expectNonNegative(matchNNInteger()); 1041 expectChar(':'); 1042 while (checkKeyword("typearg")) { 1043 matchKeyword("typearg"); 1044 if (checkChar('#')) { matchChar('#'); } 1045 int type_index = expectNonNegative(matchNNInteger()); 1046 RelativeLocation loc = isOffset 1047 ? RelativeLocation.createOffset(i, type_index) 1048 : RelativeLocation.createIndex(i, type_index); 1049 ATypeElement t = exp.calls.vivify(loc); 1050 expectChar(':'); 1051 parseAnnotations(t); 1052 parseInnerTypes(t); 1053 } 1054 } 1055 while (checkKeyword("reference")) { 1056 matchKeyword("reference"); 1057 matched = true; 1058 evermatched = true; 1059 ATypeElement t; 1060 RelativeLocation loc; 1061 int i; 1062 boolean isOffset = checkChar('#'); 1063 if (isOffset) { 1064 expectChar('#'); 1065 i = expectNonNegative(matchNNInteger()); 1066 loc = RelativeLocation.createOffset(i, 0); 1067 } else { 1068 expectChar('*'); 1069 i = expectNonNegative(matchNNInteger()); 1070 loc = RelativeLocation.createIndex(i, 0); 1071 } 1072 expectChar(':'); 1073 t = exp.refs.vivify(loc); 1074 parseAnnotations(t); 1075 parseInnerTypes(t); 1076 while (checkKeyword("typearg")) { 1077 matchKeyword("typearg"); 1078 if (checkChar('#')) { matchChar('#'); } 1079 int type_index = expectNonNegative(matchNNInteger()); 1080 loc = isOffset 1081 ? RelativeLocation.createOffset(i, type_index) 1082 : RelativeLocation.createIndex(i, type_index); 1083 t = exp.refs.vivify(loc); 1084 expectChar(':'); 1085 parseAnnotations(t); 1086 parseInnerTypes(t); 1087 } 1088 } 1089 while (checkKeyword("lambda")) { 1090 matchKeyword("lambda"); 1091 matched = true; 1092 evermatched = true; 1093 RelativeLocation loc; 1094 if (checkChar('#')) { 1095 expectChar('#'); 1096 int offset = expectNonNegative(matchNNInteger()); 1097 int type_index = 0; 1098 if (checkChar(',')) { 1099 expectChar(','); 1100 type_index = expectNonNegative(matchNNInteger()); 1101 } 1102 loc = RelativeLocation.createOffset(offset, type_index); 1103 } else { 1104 expectChar('*'); 1105 int index = expectNonNegative(matchNNInteger()); 1106 int type_index = 0; 1107 if (checkChar(',')) { 1108 expectChar(','); 1109 type_index = expectNonNegative(matchNNInteger()); 1110 } 1111 loc = RelativeLocation.createIndex(index, type_index); 1112 } 1113 AMethod m = exp.funs.vivify(loc); 1114 expectChar(':'); 1115 // parseAnnotations(m); 1116 parseLambda(m); 1117 // parseMethod(m); 1118 } 1119 } 1120 return evermatched; 1121 } 1122 1123 private static boolean isTypeSelector(String selector) { 1124 return Arrays.<String>binarySearch(typeSelectors, selector, Collator.getInstance()) >= 0; 1125 } 1126 1127 private static boolean selectsExpression(ASTPath astPath) { 1128 int n = astPath.size(); 1129 if (--n >= 0) { 1130 ASTPath.ASTEntry entry = astPath.get(n); 1131 while (--n >= 0 && entry.getTreeKind() == Kind.MEMBER_SELECT 1132 && entry.childSelectorIs(ASTPath.EXPRESSION)) { 1133 entry = astPath.get(n); 1134 } 1135 return !isTypeSelector(entry.getChildSelector()); 1136 } 1137 return false; 1138 } 1139 1140 private boolean parseASTInsertions(ADeclaration decl) 1141 throws IOException, ParseException { 1142 boolean matched = false; 1143 while (checkKeyword("insert-annotation")) { 1144 matched = true; 1145 matchKeyword("insert-annotation"); 1146 ASTPath astPath = parseASTPath(); 1147 expectChar(':'); 1148 // if path doesn't indicate a type, a cast must be generated 1149 if (selectsExpression(astPath)) { 1150 ATypeElementWithType i = decl.insertTypecasts.vivify(astPath); 1151 parseAnnotations(i); 1152 i.setType(new DeclaredType()); 1153 parseInnerTypes(i); 1154 } else { 1155 // astPath = fixNewArrayType(astPath); // handle special case 1156 // ATypeElement i = decl.insertAnnotations.vivify(astPath); 1157 // parseAnnotations(i); 1158 // parseInnerTypes(i); 1159 int offset = 0; 1160 Pair<ASTPath, InnerTypeLocation> pair = 1161 splitNewArrayType(astPath); // handle special case 1162 ATypeElement i; 1163 if (pair == null) { 1164 i = decl.insertAnnotations.vivify(astPath); 1165 } else { 1166 i = decl.insertAnnotations.vivify(pair.a); 1167 if (pair.b != null) { 1168 i = i.innerTypes.vivify(pair.b); 1169 offset = pair.b.location.size(); 1170 } 1171 } 1172 parseAnnotations(i); 1173 parseInnerTypes(i, offset); 1174 } 1175 } 1176 while (checkKeyword("insert-typecast")) { 1177 matched = true; 1178 matchKeyword("insert-typecast"); 1179 ASTPath astPath = parseASTPath(); 1180 expectChar(':'); 1181 ATypeElementWithType i = decl.insertTypecasts.vivify(astPath); 1182 parseAnnotations(i); 1183 Type type = parseType(); 1184 i.setType(type); 1185 parseInnerTypes(i); 1186 } 1187 return matched; 1188 } 1189 1190 // Due to the unfortunate representation of new array expressions, 1191 // ASTPaths to their inner array types break the usual rule that 1192 // an ASTPath corresponds to an AST node. This method restores the 1193 // invariant by separating out the inner type information. 1194 private Pair<ASTPath, InnerTypeLocation> splitNewArrayType(ASTPath astPath) { 1195 ASTPath outerPath = astPath; 1196 InnerTypeLocation loc = null; 1197 int last = astPath.size() - 1; 1198 1199 if (last > 0) { 1200 ASTPath.ASTEntry entry = astPath.get(last); 1201 if (entry.getTreeKind() == Kind.NEW_ARRAY && entry.childSelectorIs(ASTPath.TYPE)) { 1202 int a = entry.getArgument(); 1203 if (a > 0) { 1204 outerPath = astPath.getParentPath().extend(new ASTPath.ASTEntry(Kind.NEW_ARRAY, ASTPath.TYPE, 0)); 1205 loc = new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(Collections.nCopies(2 * a, 0))); 1206 } 1207 } 1208 } 1209 1210 return Pair.of(outerPath, loc); 1211 } 1212 1213 private ASTPath fixNewArrayType(ASTPath astPath) { 1214 ASTPath outerPath = astPath; 1215 int last = astPath.size() - 1; 1216 1217 if (last > 0) { 1218 ASTPath.ASTEntry entry = astPath.get(last); 1219 if (entry.getTreeKind() == Kind.NEW_ARRAY && entry.childSelectorIs(ASTPath.TYPE)) { 1220 int a = entry.getArgument(); 1221 outerPath = astPath.getParentPath().extend(new ASTPath.ASTEntry(Kind.NEW_ARRAY, ASTPath.TYPE, 0)); 1222 while (--a >= 0) { 1223 outerPath = outerPath.extend(new ASTPath.ASTEntry(Kind.ARRAY_TYPE, ASTPath.TYPE)); 1224 } 1225 } 1226 } 1227 1228 return outerPath; 1229 } 1230 1231 /** 1232 * Parses an AST path. 1233 * @return the AST path 1234 */ 1235 private ASTPath parseASTPath() throws IOException, ParseException { 1236 ASTPath astPath = ASTPath.empty().extend(parseASTEntry()); 1237 while (matchChar(',')) { 1238 astPath = astPath.extend(parseASTEntry()); 1239 } 1240 return astPath; 1241 } 1242 1243 /** 1244 * Parses and returns the next AST entry. 1245 * @return a new AST entry 1246 * @throws ParseException if the next entry type is invalid 1247 */ 1248 private ASTPath.ASTEntry parseASTEntry() throws IOException, ParseException { 1249 ASTPath.ASTEntry entry; 1250 if (matchKeyword("AnnotatedType")) { 1251 entry = newASTEntry(Kind.ANNOTATED_TYPE, new String[] {ASTPath.ANNOTATION, ASTPath.UNDERLYING_TYPE}, 1252 new String[] {ASTPath.ANNOTATION}); 1253 } else if (matchKeyword("ArrayAccess")) { 1254 entry = newASTEntry(Kind.ARRAY_ACCESS, new String[] {ASTPath.EXPRESSION, ASTPath.INDEX}); 1255 } else if (matchKeyword("ArrayType")) { 1256 entry = newASTEntry(Kind.ARRAY_TYPE, new String[] {ASTPath.TYPE}); 1257 } else if (matchKeyword("Assert")) { 1258 entry = newASTEntry(Kind.ASSERT, new String[] {ASTPath.CONDITION, ASTPath.DETAIL}); 1259 } else if (matchKeyword("Assignment")) { 1260 entry = newASTEntry(Kind.ASSIGNMENT, new String[] {ASTPath.VARIABLE, ASTPath.EXPRESSION}); 1261 } else if (matchKeyword("Binary")) { 1262 // Always use Kind.PLUS for Binary 1263 entry = newASTEntry(Kind.PLUS, new String[] {ASTPath.LEFT_OPERAND, ASTPath.RIGHT_OPERAND}); 1264 } else if (matchKeyword("Block")) { 1265 entry = newASTEntry(Kind.BLOCK, new String[] {ASTPath.STATEMENT}, new String[] {ASTPath.STATEMENT}); 1266 } else if (matchKeyword("Case")) { 1267 entry = newASTEntry(Kind.CASE, new String[] {ASTPath.EXPRESSION, ASTPath.STATEMENT}, 1268 new String[] {ASTPath.STATEMENT}); 1269 } else if (matchKeyword("Catch")) { 1270 entry = newASTEntry(Kind.CATCH, new String[] {ASTPath.PARAMETER, ASTPath.BLOCK}); 1271 } else if (matchKeyword("Class")) { 1272 entry = newASTEntry(Kind.CLASS, 1273 new String[] {ASTPath.BOUND, ASTPath.INITIALIZER, ASTPath.TYPE_PARAMETER}, 1274 new String[] {ASTPath.BOUND, ASTPath.INITIALIZER, ASTPath.TYPE_PARAMETER}); 1275 } else if (matchKeyword("CompoundAssignment")) { 1276 // Always use Kind.PLUS_ASSIGNMENT for CompoundAssignment 1277 entry = newASTEntry(Kind.PLUS_ASSIGNMENT, new String[] {ASTPath.VARIABLE, ASTPath.EXPRESSION}); 1278 } else if (matchKeyword("ConditionalExpression")) { 1279 entry = newASTEntry(Kind.CONDITIONAL_EXPRESSION, 1280 new String[] {ASTPath.CONDITION, ASTPath.TRUE_EXPRESSION, ASTPath.FALSE_EXPRESSION}); 1281 } else if (matchKeyword("DoWhileLoop")) { 1282 entry = newASTEntry(Kind.DO_WHILE_LOOP, new String[] {ASTPath.CONDITION, ASTPath.STATEMENT}); 1283 } else if (matchKeyword("EnhancedForLoop")) { 1284 entry = newASTEntry(Kind.ENHANCED_FOR_LOOP, new String[] {ASTPath.VARIABLE, ASTPath.EXPRESSION, ASTPath.STATEMENT}); 1285 } else if (matchKeyword("ExpressionStatement")) { 1286 entry = newASTEntry(Kind.EXPRESSION_STATEMENT, new String[] {ASTPath.EXPRESSION}); 1287 } else if (matchKeyword("ForLoop")) { 1288 entry = newASTEntry(Kind.FOR_LOOP, new String[] {ASTPath.INITIALIZER, ASTPath.CONDITION, ASTPath.UPDATE, ASTPath.STATEMENT}, 1289 new String[] {ASTPath.INITIALIZER, ASTPath.UPDATE}); 1290 } else if (matchKeyword("If")) { 1291 entry = newASTEntry(Kind.IF, new String[] {ASTPath.CONDITION, ASTPath.THEN_STATEMENT, ASTPath.ELSE_STATEMENT}); 1292 } else if (matchKeyword("InstanceOf")) { 1293 entry = newASTEntry(Kind.INSTANCE_OF, new String[] {ASTPath.EXPRESSION, ASTPath.TYPE}); 1294 } else if (matchKeyword("LabeledStatement")) { 1295 entry = newASTEntry(Kind.LABELED_STATEMENT, new String[] {ASTPath.STATEMENT}); 1296 } else if (matchKeyword("LambdaExpression")) { 1297 entry = newASTEntry(Kind.LAMBDA_EXPRESSION, new String[] {ASTPath.PARAMETER, ASTPath.BODY}, 1298 new String[] {ASTPath.PARAMETER}); 1299 } else if (matchKeyword("MemberReference")) { 1300 entry = newASTEntry(Kind.MEMBER_REFERENCE, new String[] {ASTPath.QUALIFIER_EXPRESSION, ASTPath.TYPE_ARGUMENT}, 1301 new String[] {ASTPath.TYPE_ARGUMENT}); 1302 } else if (matchKeyword("MemberSelect")) { 1303 entry = newASTEntry(Kind.MEMBER_SELECT, new String[] {ASTPath.EXPRESSION}); 1304 } else if (matchKeyword("Method")) { 1305 entry = newASTEntry(Kind.METHOD, new String[] {ASTPath.BODY, ASTPath.TYPE, ASTPath.PARAMETER, ASTPath.TYPE_PARAMETER}, 1306 new String[] {ASTPath.PARAMETER, ASTPath.TYPE_PARAMETER}); 1307 } else if (matchKeyword("MethodInvocation")) { 1308 entry = newASTEntry(Kind.METHOD_INVOCATION, new String[] {ASTPath.TYPE_ARGUMENT, ASTPath.METHOD_SELECT, ASTPath.ARGUMENT}, 1309 new String[] {ASTPath.TYPE_ARGUMENT, ASTPath.ARGUMENT}); 1310 } else if (matchKeyword("NewArray")) { 1311 entry = newASTEntry(Kind.NEW_ARRAY, new String[] {ASTPath.TYPE, ASTPath.DIMENSION, ASTPath.INITIALIZER}, 1312 new String[] {ASTPath.TYPE, ASTPath.DIMENSION, ASTPath.INITIALIZER}); 1313 } else if (matchKeyword("NewClass")) { 1314 entry = newASTEntry(Kind.NEW_CLASS, new String[] {ASTPath.ENCLOSING_EXPRESSION, ASTPath.TYPE_ARGUMENT, ASTPath.IDENTIFIER, ASTPath.ARGUMENT, ASTPath.CLASS_BODY}, 1315 new String[] {ASTPath.TYPE_ARGUMENT, ASTPath.ARGUMENT}); 1316 } else if (matchKeyword("ParameterizedType")) { 1317 entry = newASTEntry(Kind.PARAMETERIZED_TYPE, new String[] {ASTPath.TYPE, ASTPath.TYPE_ARGUMENT}, 1318 new String[] {ASTPath.TYPE_ARGUMENT}); 1319 } else if (matchKeyword("Parenthesized")) { 1320 entry = newASTEntry(Kind.PARENTHESIZED, new String[] {ASTPath.EXPRESSION}); 1321 } else if (matchKeyword("Return")) { 1322 entry = newASTEntry(Kind.RETURN, new String[] {ASTPath.EXPRESSION}); 1323 } else if (matchKeyword("Switch")) { 1324 entry = newASTEntry(Kind.SWITCH, new String[] {ASTPath.EXPRESSION, ASTPath.CASE}, 1325 new String[] {ASTPath.CASE}); 1326 } else if (matchKeyword("Synchronized")) { 1327 entry = newASTEntry(Kind.SYNCHRONIZED, new String[] {ASTPath.EXPRESSION, ASTPath.BLOCK}); 1328 } else if (matchKeyword("Throw")) { 1329 entry = newASTEntry(Kind.THROW, new String[] {ASTPath.EXPRESSION}); 1330 } else if (matchKeyword("Try")) { 1331 entry = newASTEntry(Kind.TRY, new String[] {ASTPath.BLOCK, ASTPath.CATCH, ASTPath.FINALLY_BLOCK}, 1332 new String[] {ASTPath.CATCH}); 1333 } else if (matchKeyword("TypeCast")) { 1334 entry = newASTEntry(Kind.TYPE_CAST, new String[] {ASTPath.TYPE, ASTPath.EXPRESSION}); 1335 } else if (matchKeyword("TypeParameter")) { 1336 entry = newASTEntry(Kind.TYPE_PARAMETER, new String[] {ASTPath.BOUND}, 1337 new String[] {ASTPath.BOUND}); 1338 } else if (matchKeyword("Unary")) { 1339 // Always use Kind.UNARY_PLUS for Unary 1340 entry = newASTEntry(Kind.UNARY_PLUS, new String[] {ASTPath.EXPRESSION}); 1341 } else if (matchKeyword("UnionType")) { 1342 entry = newASTEntry(Kind.UNION_TYPE, new String[] {ASTPath.TYPE_ALTERNATIVE}, 1343 new String[] {ASTPath.TYPE_ALTERNATIVE}); 1344 } else if (matchKeyword("Variable")) { 1345 entry = newASTEntry(Kind.VARIABLE, new String[] {ASTPath.TYPE, ASTPath.INITIALIZER}); 1346 } else if (matchKeyword("WhileLoop")) { 1347 entry = newASTEntry(Kind.WHILE_LOOP, new String[] {ASTPath.CONDITION, ASTPath.STATEMENT}); 1348 } else if (matchKeyword("Wildcard")) { 1349 // Always use Kind.UNBOUNDED_WILDCARD for Wildcard 1350 entry = newASTEntry(Kind.UNBOUNDED_WILDCARD, new String[] {ASTPath.BOUND}); 1351 } else { 1352 throw new ParseException("Invalid AST path type: " + st.sval); 1353 } 1354 return entry; 1355 } 1356 1357 /** 1358 * Parses and constructs a new AST entry, where none of the child selections require 1359 * arguments. For example, the call: 1360 * 1361 * <pre> 1362 * {@code newASTEntry(Kind.WHILE_LOOP, new String[] {"condition", "statement"});</pre> 1363 * 1364 * constructs a while loop AST entry, where the valid child selectors are "condition" or 1365 * "statement". 1366 * 1367 * @param kind the kind of this AST entry 1368 * @param legalChildSelectors a list of the legal child selectors for this AST entry 1369 * @return a new {@link ASTPath.ASTEntry} 1370 * @throws ParseException if an illegal argument is found 1371 */ 1372 private ASTPath.ASTEntry newASTEntry(Kind kind, String[] legalChildSelectors) throws IOException, ParseException { 1373 return newASTEntry(kind, legalChildSelectors, null); 1374 } 1375 1376 /** 1377 * Parses and constructs a new AST entry. For example, the call: 1378 * 1379 * <pre> 1380 * {@code newASTEntry(Kind.CASE, new String[] {"expression", "statement"}, new String[] {"statement"}); 1381 * </pre> 1382 * 1383 * constructs a case AST entry, where the valid child selectors are 1384 * "expression" or "statement" and the "statement" child selector requires 1385 * an argument. 1386 * 1387 * @param kind the kind of this AST entry 1388 * @param legalChildSelectors a list of the legal child selectors for this AST entry 1389 * @param argumentChildSelectors a list of the child selectors that also require an argument. 1390 * Entries here should also be in the legalChildSelectors list. 1391 * @return a new {@link ASTPath.ASTEntry} 1392 * @throws ParseException if an illegal argument is found 1393 */ 1394 private ASTPath.ASTEntry newASTEntry(Kind kind, String[] legalChildSelectors, String[] argumentChildSelectors) throws IOException, ParseException { 1395 expectChar('.'); 1396 for (String arg : legalChildSelectors) { 1397 if (matchKeyword(arg)) { 1398 if (argumentChildSelectors != null && ArraysMDE.indexOf(argumentChildSelectors, arg) >= 0) { 1399 int index = matchNNInteger(); 1400 return new ASTPath.ASTEntry(kind, arg, index); 1401 } else { 1402 return new ASTPath.ASTEntry(kind, arg); 1403 } 1404 } 1405 } 1406 throw new ParseException("Invalid argument for " + kind + " (legal arguments - " + Arrays.toString(legalChildSelectors) + "): " + st.sval); 1407 } 1408 1409 /** 1410 * Parses the next tokens as a Java type. 1411 */ 1412 private Type parseType() throws IOException, ParseException { 1413 DeclaredType declaredType; 1414 if (matchChar('?')) { 1415 declaredType = new DeclaredType(DeclaredType.WILDCARD); 1416 } else { 1417 declaredType = parseDeclaredType(); 1418 } 1419 if (checkKeyword("extends") || checkKeyword("super")) { 1420 return parseBoundedType(declaredType); 1421 } else { 1422 Type type = declaredType; 1423 while (matchChar('[')) { 1424 expectChar(']'); 1425 type = new ArrayType(type); 1426 } 1427 return type; 1428 } 1429 } 1430 1431 /** 1432 * Parses the next tokens as a declared type. 1433 */ 1434 private DeclaredType parseDeclaredType() throws IOException, ParseException { 1435 String type = matchIdentifier(); 1436 if (type == null) { 1437 type = matchPrimitiveType(); 1438 if (type == null) { 1439 throw new ParseException("Expected identifier or primitive type"); 1440 } 1441 } 1442 return parseDeclaredType(type); 1443 } 1444 1445 /** 1446 * Parses the next tokens as a declared type. 1447 * @param name the name of the initial identifier 1448 */ 1449 private DeclaredType parseDeclaredType(String name) throws IOException, ParseException { 1450 DeclaredType type = new DeclaredType(name); 1451 if (matchChar('<')) { 1452 type.addTypeParameter(parseType()); 1453 while (matchChar(',')) { 1454 type.addTypeParameter(parseType()); 1455 } 1456 expectChar('>'); 1457 } 1458 if (matchChar('.')) { 1459 type.setInnerType(parseDeclaredType()); 1460 } 1461 return type; 1462 } 1463 1464 /** 1465 * Parses the next tokens as a bounded type. 1466 * @param type the name, which precedes "extends" or "super" 1467 */ 1468 private BoundedType parseBoundedType(DeclaredType type) throws IOException, ParseException { 1469 BoundKind kind; 1470 if (matchKeyword("extends")) { 1471 kind = BoundKind.EXTENDS; 1472 } else if (matchKeyword("super")) { 1473 kind = BoundKind.SUPER; 1474 } else { 1475 throw new ParseException("Illegal bound kind: " + st.sval); 1476 } 1477 return new BoundedType(type, kind, parseDeclaredType()); 1478 } 1479 1480 private void parseClass() throws IOException, ParseException { 1481 expectKeyword("class"); 1482 String basename = expectIdentifier(); 1483 String fullName = curPkgPrefix + basename; 1484 1485 AClass c = scene.classes.vivify(fullName); 1486 expectChar(':'); 1487 1488 parseAnnotations(c); 1489 parseBounds(c.bounds); 1490 1491 while (checkKeyword("extends")) { 1492 parseExtends(c); 1493 } 1494 while (checkKeyword("implements")) { 1495 parseImplements(c); 1496 } 1497 parseASTInsertions(c); 1498 1499 while (checkKeyword("field")) { 1500 parseField(c); 1501 } 1502 while (checkKeyword("staticinit")) { 1503 parseStaticInit(c); 1504 } 1505 while (checkKeyword("instanceinit")) { 1506 parseInstanceInit(c); 1507 } 1508 while (checkKeyword("method")) { 1509 parseMethod(c); 1510 } 1511 c.methods.prune(); 1512 } 1513 1514 // Reads the index file in this.st and puts the information in this.scene. 1515 private void parse() throws ParseException, IOException { 1516 st.nextToken(); 1517 1518 while (st.ttype != TT_EOF) { 1519 expectKeyword("package"); 1520 1521 String pkg; 1522 if (checkIdentifier() == null) { 1523 pkg = null; 1524 // the default package cannot be annotated 1525 matchChar(':'); 1526 } else { 1527 pkg = expectQualifiedName(); 1528 // AElement p = scene.packages.vivify(pkg); 1529 AClass p = scene.classes.vivify(pkg + ".package-info"); 1530 expectChar(':'); 1531 p = scene.classes.vivify(pkg + ".package-info"); 1532 parseAnnotations(p); 1533 } 1534 1535 if (pkg != null) { 1536 curPkgPrefix = pkg + "."; 1537 } else { 1538 curPkgPrefix = ""; 1539 } 1540 1541 for (;;) { 1542 if (checkKeyword("annotation")) { 1543 parseAnnotationDef(); 1544 } else if (checkKeyword("class")) { 1545 parseClass(); 1546 } else if (checkKeyword("package") || st.ttype == TT_EOF) { 1547 break; 1548 } else { 1549 throw new ParseException("Expected: `annotation', `class', or `package'. Found: `" 1550 + st.sval + "', ttype:" + st.ttype); 1551 } 1552 } 1553 } 1554 1555/* 1556 for (Map.Entry<String, AnnotationDef> entry : defs.entrySet()) { 1557 final String annotationType = entry.getKey(); 1558 AnnotationDef def = entry.getValue(); 1559 for (AnnotationFieldType aft : def.fieldTypes.values()) { 1560 aft.accept(new AFTVisitor<Void, Void>() { 1561 @Override 1562 public Void visitAnnotationAFT(AnnotationAFT aft, 1563 Void arg) { 1564 for (AnnotationFieldType t : aft.annotationDef.fieldTypes.values()) { 1565 t.accept(this, arg); 1566 } 1567 return null; 1568 } 1569 1570 @Override 1571 public Void visitArrayAFT(ArrayAFT aft, Void arg) { 1572 return aft.elementType == null ? null 1573 : aft.elementType.accept(this, arg); 1574 } 1575 1576 @Override 1577 public Void visitBasicAFT(BasicAFT aft, Void arg) { 1578 return null; 1579 } 1580 1581 @Override 1582 public Void visitClassTokenAFT(ClassTokenAFT aft, Void arg) { 1583 return null; 1584 } 1585 1586 @Override 1587 public Void visitEnumAFT(EnumAFT aft, Void arg) { 1588 importSet(annotationType, aft).add(aft.typeName); 1589 return null; 1590 } 1591 1592 private Set<String> importSet(final String annotationType, 1593 AnnotationFieldType aft) { 1594 Set<String> imps = scene.imports.get(annotationType); 1595 if (imps == null) { 1596 imps = new TreeSet<String>(); 1597 scene.imports.put(annotationType, imps); 1598 } 1599 return imps; 1600 } 1601 }, null); 1602 } 1603 } 1604*/ 1605 } 1606 1607 private IndexFileParser(Reader in, AScene scene) { 1608 defs = new LinkedHashMap<String, AnnotationDef>(); 1609 for (AnnotationDef ad : Annotations.standardDefs) { 1610 try { 1611 addDef(ad); 1612 } catch (ParseException e) { 1613 throw new Error(e); 1614 } 1615 } 1616 1617 st = new StreamTokenizer(in); 1618 st.slashSlashComments(true); 1619 1620 // restrict numbers -- don't really need, could interfere with 1621 // annotation values 1622 // st.ordinaryChar('-'); 1623 // HMMM this fixes fully-qualified-name strangeness but breaks 1624 // floating-point numbers 1625 st.ordinaryChar('.'); 1626 1627 // argggh!!! stupid default needs to be overridden! see java bug 4217680 1628 st.ordinaryChar('/'); 1629 1630 // for "type-argument" 1631 st.wordChars('-', '-'); 1632 1633 // java identifiers can contain numbers, _, and $ 1634 st.wordChars('0', '9'); 1635 st.wordChars('_', '_'); 1636 st.wordChars('$', '$'); 1637 1638 this.scene = scene; 1639 1640 // See if the nonnull analysis picks up on this: 1641 // curPkgPrefix == ""; // will get changed later anyway 1642 } 1643 1644 /** 1645 * Reads annotations from <code>in</code> in index file format and merges 1646 * them into <code>scene</code>. Annotations 1647 * from the input are merged into the scene; it is an error if both the 1648 * scene and the input contain annotations of the same type on the same 1649 * element. 1650 * 1651 * <p> 1652 * Since each annotation in a scene carries its own definition and the 1653 * scene as a whole no longer has a set of definitions, annotation 1654 * definitions that are given in the input but never used are not saved 1655 * anywhere and will not be included if the scene is written back to an 1656 * index file. Similarly, retention policies on definitions of annotations 1657 * that are never used at the top level are dropped. 1658 * 1659 * <p> 1660 * Caveat: Parsing of floating point numbers currently does not work. 1661 */ 1662 public static Map<String, AnnotationDef> parse(LineNumberReader in, 1663 AScene scene) throws IOException, ParseException { 1664 IndexFileParser parser = new IndexFileParser(in, scene); 1665 // no filename is available in the exception messages 1666 return parseAndReturnAnnotationDefs(null, in, parser); 1667 } 1668 1669 /** 1670 * Reads annotations from the index file <code>filename</code> and merges 1671 * them into <code>scene</code>; see {@link #parse(LineNumberReader, AScene)}. 1672 */ 1673 public static Map<String, AnnotationDef> parseFile(String filename, 1674 AScene scene) throws IOException { 1675 LineNumberReader in = new LineNumberReader(new FileReader(filename)); 1676 IndexFileParser parser = new IndexFileParser(in, scene); 1677 return parseAndReturnAnnotationDefs(filename, in, parser); 1678 } 1679 1680 /** 1681 * Reads annotations from the string (in index file format) and merges 1682 * them into <code>scene</code>; see {@link #parse(LineNumberReader, AScene)}. 1683 * Primarily for testing. 1684 */ 1685 public static Map<String, AnnotationDef> parseString(String fileContents, 1686 AScene scene) throws IOException { 1687 String filename = 1688 "While parsing string: \n----------------BEGIN----------------\n" 1689 + fileContents + "----------------END----------------\n"; 1690 LineNumberReader in = new LineNumberReader( 1691 new StringReader(fileContents)); 1692 IndexFileParser parser = new IndexFileParser(in, scene); 1693 return parseAndReturnAnnotationDefs(filename, in, parser); 1694 } 1695 1696 private static Map<String, AnnotationDef> parseAndReturnAnnotationDefs( 1697 String filename, LineNumberReader in, IndexFileParser parser) 1698 throws IOException { 1699 try { 1700 parser.parse(); 1701 return Collections.unmodifiableMap(parser.defs); 1702 } catch (IOException e) { 1703 throw filename == null ? new FileIOException(in, e) 1704 : new FileIOException(in, filename, e); 1705 } catch (ParseException e) { 1706 throw filename == null ? new FileIOException(in, e) 1707 : new FileIOException(in, filename, e); 1708 } 1709 } 1710 1711 /** 1712 * Parse the given text into a {@link Type}. 1713 * @param text the text to parse 1714 * @return the type 1715 */ 1716 public static Type parseType(String text) { 1717 StringReader in = new StringReader(text); 1718 IndexFileParser parser = new IndexFileParser(in, null); 1719 try { 1720 parser.st.nextToken(); 1721 return parser.parseType(); 1722 } catch (Exception e) { 1723 throw new RuntimeException("Error parsing type from: '" + text + "'", e); 1724 } 1725 } 1726} 1727