1 /* 2 * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javac.code; 27 28 import com.sun.tools.javac.code.Kinds.Kind; 29 import java.util.*; 30 31 import com.sun.tools.javac.code.Symbol.TypeSymbol; 32 import com.sun.tools.javac.tree.JCTree.JCImport; 33 import com.sun.tools.javac.util.*; 34 import com.sun.tools.javac.util.List; 35 36 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE; 37 import static com.sun.tools.javac.code.Scope.LookupKind.RECURSIVE; 38 import java.util.stream.Stream; 39 import java.util.stream.StreamSupport; 40 41 /** A scope represents an area of visibility in a Java program. The 42 * Scope class is a container for symbols which provides 43 * efficient access to symbols given their names. Scopes are implemented 44 * as hash tables with "open addressing" and "double hashing". 45 * Scopes can be nested. Nested scopes can share their hash tables. 46 * 47 * <p><b>This is NOT part of any supported API. 48 * If you write code that depends on this, you do so at your own risk. 49 * This code and its internal interfaces are subject to change or 50 * deletion without notice.</b> 51 */ 52 public abstract class Scope { 53 54 /** The scope's owner. 55 */ 56 public final Symbol owner; 57 58 protected Scope(Symbol owner) { 59 this.owner = owner; 60 } 61 62 /**Returns all Symbols in this Scope. Symbols from outward Scopes are included. 63 */ 64 public final Iterable<Symbol> getSymbols() { 65 return getSymbols(noFilter); 66 } 67 68 /**Returns Symbols that match the given filter. Symbols from outward Scopes are included. 69 */ 70 public final Iterable<Symbol> getSymbols(Filter<Symbol> sf) { 71 return getSymbols(sf, RECURSIVE); 72 } 73 74 /**Returns all Symbols in this Scope. Symbols from outward Scopes are included 75 * iff lookupKind == RECURSIVE. 76 */ 77 public final Iterable<Symbol> getSymbols(LookupKind lookupKind) { 78 return getSymbols(noFilter, lookupKind); 79 } 80 81 /**Returns Symbols that match the given filter. Symbols from outward Scopes are included 82 * iff lookupKind == RECURSIVE. 83 */ 84 public abstract Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind); 85 86 /**Returns Symbols with the given name. Symbols from outward Scopes are included. 87 */ 88 public final Iterable<Symbol> getSymbolsByName(Name name) { 89 return getSymbolsByName(name, RECURSIVE); 90 } 91 92 /**Returns Symbols with the given name that match the given filter. 93 * Symbols from outward Scopes are included. 94 */ 95 public final Iterable<Symbol> getSymbolsByName(final Name name, final Filter<Symbol> sf) { 96 return getSymbolsByName(name, sf, RECURSIVE); 97 } 98 99 /**Returns Symbols with the given name. Symbols from outward Scopes are included 100 * iff lookupKind == RECURSIVE. 101 */ 102 public final Iterable<Symbol> getSymbolsByName(Name name, LookupKind lookupKind) { 103 return getSymbolsByName(name, noFilter, lookupKind); 104 } 105 106 /**Returns Symbols with the given name that match the given filter. 107 * Symbols from outward Scopes are included iff lookupKind == RECURSIVE. 108 */ 109 public abstract Iterable<Symbol> getSymbolsByName(final Name name, final Filter<Symbol> sf, 110 final LookupKind lookupKind); 111 112 /** Return the first Symbol from this or outward scopes with the given name. 113 * Returns null if none. 114 */ 115 public final Symbol findFirst(Name name) { 116 return findFirst(name, noFilter); 117 } 118 119 /** Return the first Symbol from this or outward scopes with the given name that matches the 120 * given filter. Returns null if none. 121 */ 122 public Symbol findFirst(Name name, Filter<Symbol> sf) { 123 Iterator<Symbol> it = getSymbolsByName(name, sf).iterator(); 124 return it.hasNext() ? it.next() : null; 125 } 126 127 /** Returns true iff there are is at least one Symbol in this scope matching the given filter. 128 * Does not inspect outward scopes. 129 */ 130 public boolean anyMatch(Filter<Symbol> filter) { 131 return getSymbols(filter, NON_RECURSIVE).iterator().hasNext(); 132 } 133 134 /** Returns true iff the given Symbol is in this scope or any outward scope. 135 */ 136 public boolean includes(final Symbol sym) { 137 return getSymbolsByName(sym.name, new Filter<Symbol>() { 138 @Override 139 public boolean accepts(Symbol t) { 140 return t == sym; 141 } 142 }).iterator().hasNext(); 143 } 144 145 /** Returns true iff this scope does not contain any Symbol. Does not inspect outward scopes. 146 */ 147 public boolean isEmpty() { 148 return !getSymbols(NON_RECURSIVE).iterator().hasNext(); 149 } 150 151 /** Returns the Scope from which the givins Symbol originates in this scope. 152 */ 153 public abstract Scope getOrigin(Symbol byName); 154 155 /** Returns true iff the given Symbol is part of this scope due to a static import. 156 */ 157 public abstract boolean isStaticallyImported(Symbol byName); 158 159 private static final Filter<Symbol> noFilter = null; 160 161 /** A list of scopes to be notified if items are to be removed from this scope. 162 */ 163 List<ScopeListener> listeners = List.nil(); 164 165 public void addScopeListener(ScopeListener sl) { 166 listeners = listeners.prepend(sl); 167 } 168 169 public interface ScopeListener { 170 public void symbolAdded(Symbol sym, Scope s); 171 public void symbolRemoved(Symbol sym, Scope s); 172 } 173 174 public enum LookupKind { 175 RECURSIVE, 176 NON_RECURSIVE; 177 } 178 179 /**A scope into which Symbols can be added.*/ 180 public abstract static class WriteableScope extends Scope { 181 182 public WriteableScope(Symbol owner) { 183 super(owner); 184 } 185 186 /** Enter the given Symbol into this scope. 187 */ 188 public abstract void enter(Symbol c); 189 /** Enter symbol sym in this scope if not already there. 190 */ 191 public abstract void enterIfAbsent(Symbol c); 192 193 public abstract void remove(Symbol c); 194 195 /** Construct a fresh scope within this scope, with same owner. The new scope may 196 * shares internal structures with the this scope. Used in connection with 197 * method leave if scope access is stack-like in order to avoid allocation 198 * of fresh tables. 199 */ 200 public final WriteableScope dup() { 201 return dup(this.owner); 202 } 203 204 /** Construct a fresh scope within this scope, with new owner. The new scope may 205 * shares internal structures with the this scope. Used in connection with 206 * method leave if scope access is stack-like in order to avoid allocation 207 * of fresh tables. 208 */ 209 public abstract WriteableScope dup(Symbol newOwner); 210 211 /** Must be called on dup-ed scopes to be able to work with the outward scope again. 212 */ 213 public abstract WriteableScope leave(); 214 215 /** Construct a fresh scope within this scope, with same owner. The new scope 216 * will not share internal structures with this scope. 217 */ 218 public final WriteableScope dupUnshared() { 219 return dupUnshared(owner); 220 } 221 222 /** Construct a fresh scope within this scope, with new owner. The new scope 223 * will not share internal structures with this scope. 224 */ 225 public abstract WriteableScope dupUnshared(Symbol newOwner); 226 227 /** Create a new WriteableScope. 228 */ 229 public static WriteableScope create(Symbol owner) { 230 return new ScopeImpl(owner); 231 } 232 233 } 234 235 private static class ScopeImpl extends WriteableScope { 236 /** The number of scopes that share this scope's hash table. 237 */ 238 private int shared; 239 240 /** Next enclosing scope (with whom this scope may share a hashtable) 241 */ 242 public ScopeImpl next; 243 244 /** A hash table for the scope's entries. 245 */ 246 Entry[] table; 247 248 /** Mask for hash codes, always equal to (table.length - 1). 249 */ 250 int hashMask; 251 252 /** A linear list that also contains all entries in 253 * reverse order of appearance (i.e later entries are pushed on top). 254 */ 255 public Entry elems; 256 257 /** The number of elements in this scope. 258 * This includes deleted elements, whose value is the sentinel. 259 */ 260 int nelems = 0; 261 262 /** Use as a "not-found" result for lookup. 263 * Also used to mark deleted entries in the table. 264 */ 265 private static final Entry sentinel = new Entry(null, null, null, null); 266 267 /** The hash table's initial size. 268 */ 269 private static final int INITIAL_SIZE = 0x10; 270 271 /** Construct a new scope, within scope next, with given owner, using 272 * given table. The table's length must be an exponent of 2. 273 */ 274 private ScopeImpl(ScopeImpl next, Symbol owner, Entry[] table) { 275 super(owner); 276 this.next = next; 277 Assert.check(owner != null); 278 this.table = table; 279 this.hashMask = table.length - 1; 280 } 281 282 /** Convenience constructor used for dup and dupUnshared. */ 283 private ScopeImpl(ScopeImpl next, Symbol owner, Entry[] table, int nelems) { 284 this(next, owner, table); 285 this.nelems = nelems; 286 } 287 288 /** Construct a new scope, within scope next, with given owner, 289 * using a fresh table of length INITIAL_SIZE. 290 */ 291 public ScopeImpl(Symbol owner) { 292 this(null, owner, new Entry[INITIAL_SIZE]); 293 } 294 295 /** Construct a fresh scope within this scope, with new owner, 296 * which shares its table with the outer scope. Used in connection with 297 * method leave if scope access is stack-like in order to avoid allocation 298 * of fresh tables. 299 */ 300 public WriteableScope dup(Symbol newOwner) { 301 ScopeImpl result = new ScopeImpl(this, newOwner, this.table, this.nelems); 302 shared++; 303 // System.out.println("====> duping scope " + this.hashCode() + " owned by " + newOwner + " to " + result.hashCode()); 304 // new Error().printStackTrace(System.out); 305 return result; 306 } 307 308 /** Construct a fresh scope within this scope, with new owner, 309 * with a new hash table, whose contents initially are those of 310 * the table of its outer scope. 311 */ 312 public WriteableScope dupUnshared(Symbol newOwner) { 313 if (shared > 0) { 314 //The nested Scopes might have already added something to the table, so all items 315 //that don't originate in this Scope or any of its outer Scopes need to be cleared: 316 Set<Scope> acceptScopes = Collections.newSetFromMap(new IdentityHashMap<>()); 317 ScopeImpl c = this; 318 while (c != null) { 319 acceptScopes.add(c); 320 c = c.next; 321 } 322 int n = 0; 323 Entry[] oldTable = this.table; 324 Entry[] newTable = new Entry[this.table.length]; 325 for (int i = 0; i < oldTable.length; i++) { 326 Entry e = oldTable[i]; 327 while (e != null && e != sentinel && !acceptScopes.contains(e.scope)) { 328 e = e.shadowed; 329 } 330 if (e != null) { 331 n++; 332 newTable[i] = e; 333 } 334 } 335 return new ScopeImpl(this, newOwner, newTable, n); 336 } else { 337 return new ScopeImpl(this, newOwner, this.table.clone(), this.nelems); 338 } 339 } 340 341 /** Remove all entries of this scope from its table, if shared 342 * with next. 343 */ 344 public WriteableScope leave() { 345 Assert.check(shared == 0); 346 if (table != next.table) return next; 347 while (elems != null) { 348 int hash = getIndex(elems.sym.name); 349 Entry e = table[hash]; 350 Assert.check(e == elems, elems.sym); 351 table[hash] = elems.shadowed; 352 elems = elems.sibling; 353 } 354 Assert.check(next.shared > 0); 355 next.shared--; 356 next.nelems = nelems; 357 // System.out.println("====> leaving scope " + this.hashCode() + " owned by " + this.owner + " to " + next.hashCode()); 358 // new Error().printStackTrace(System.out); 359 return next; 360 } 361 362 /** Double size of hash table. 363 */ 364 private void dble() { 365 Assert.check(shared == 0); 366 Entry[] oldtable = table; 367 Entry[] newtable = new Entry[oldtable.length * 2]; 368 for (ScopeImpl s = this; s != null; s = s.next) { 369 if (s.table == oldtable) { 370 Assert.check(s == this || s.shared != 0); 371 s.table = newtable; 372 s.hashMask = newtable.length - 1; 373 } 374 } 375 int n = 0; 376 for (int i = oldtable.length; --i >= 0; ) { 377 Entry e = oldtable[i]; 378 if (e != null && e != sentinel) { 379 table[getIndex(e.sym.name)] = e; 380 n++; 381 } 382 } 383 // We don't need to update nelems for shared inherited scopes, 384 // since that gets handled by leave(). 385 nelems = n; 386 } 387 388 /** Enter symbol sym in this scope. 389 */ 390 public void enter(Symbol sym) { 391 Assert.check(shared == 0); 392 if (nelems * 3 >= hashMask * 2) 393 dble(); 394 int hash = getIndex(sym.name); 395 Entry old = table[hash]; 396 if (old == null) { 397 old = sentinel; 398 nelems++; 399 } 400 Entry e = new Entry(sym, old, elems, this); 401 table[hash] = e; 402 elems = e; 403 404 //notify listeners 405 for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) { 406 l.head.symbolAdded(sym, this); 407 } 408 } 409 410 /** Remove symbol from this scope. 411 */ 412 public void remove(Symbol sym) { 413 Assert.check(shared == 0); 414 Entry e = lookup(sym.name, candidate -> candidate == sym); 415 if (e.scope == null) return; 416 417 // remove e from table and shadowed list; 418 int i = getIndex(sym.name); 419 Entry te = table[i]; 420 if (te == e) 421 table[i] = e.shadowed; 422 else while (true) { 423 if (te.shadowed == e) { 424 te.shadowed = e.shadowed; 425 break; 426 } 427 te = te.shadowed; 428 } 429 430 // remove e from elems and sibling list 431 te = elems; 432 if (te == e) 433 elems = e.sibling; 434 else while (true) { 435 if (te.sibling == e) { 436 te.sibling = e.sibling; 437 break; 438 } 439 te = te.sibling; 440 } 441 442 //notify listeners 443 for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) { 444 l.head.symbolRemoved(sym, this); 445 } 446 } 447 448 /** Enter symbol sym in this scope if not already there. 449 */ 450 public void enterIfAbsent(Symbol sym) { 451 Assert.check(shared == 0); 452 Entry e = lookup(sym.name); 453 while (e.scope == this && e.sym.kind != sym.kind) e = e.next(); 454 if (e.scope != this) enter(sym); 455 } 456 457 /** Given a class, is there already a class with same fully 458 * qualified name in this (import) scope? 459 */ 460 public boolean includes(Symbol c) { 461 for (Scope.Entry e = lookup(c.name); 462 e.scope == this; 463 e = e.next()) { 464 if (e.sym == c) return true; 465 } 466 return false; 467 } 468 469 /** Return the entry associated with given name, starting in 470 * this scope and proceeding outwards. If no entry was found, 471 * return the sentinel, which is characterized by having a null in 472 * both its scope and sym fields, whereas both fields are non-null 473 * for regular entries. 474 */ 475 protected Entry lookup(Name name) { 476 return lookup(name, noFilter); 477 } 478 479 protected Entry lookup(Name name, Filter<Symbol> sf) { 480 Entry e = table[getIndex(name)]; 481 if (e == null || e == sentinel) 482 return sentinel; 483 while (e.scope != null && (e.sym.name != name || (sf != null && !sf.accepts(e.sym)))) 484 e = e.shadowed; 485 return e; 486 } 487 488 public Symbol findFirst(Name name, Filter<Symbol> sf) { 489 return lookup(name, sf).sym; 490 } 491 492 /*void dump (java.io.PrintStream out) { 493 out.println(this); 494 for (int l=0; l < table.length; l++) { 495 Entry le = table[l]; 496 out.print("#"+l+": "); 497 if (le==sentinel) out.println("sentinel"); 498 else if(le == null) out.println("null"); 499 else out.println(""+le+" s:"+le.sym); 500 } 501 }*/ 502 503 /** Look for slot in the table. 504 * We use open addressing with double hashing. 505 */ 506 int getIndex (Name name) { 507 int h = name.hashCode(); 508 int i = h & hashMask; 509 // The expression below is always odd, so it is guaranteed 510 // to be mutually prime with table.length, a power of 2. 511 int x = hashMask - ((h + (h >> 16)) << 1); 512 int d = -1; // Index of a deleted item. 513 for (;;) { 514 Entry e = table[i]; 515 if (e == null) 516 return d >= 0 ? d : i; 517 if (e == sentinel) { 518 // We have to keep searching even if we see a deleted item. 519 // However, remember the index in case we fail to find the name. 520 if (d < 0) 521 d = i; 522 } else if (e.sym.name == name) 523 return i; 524 i = (i + x) & hashMask; 525 } 526 } 527 528 public boolean anyMatch(Filter<Symbol> sf) { 529 return getSymbols(sf, NON_RECURSIVE).iterator().hasNext(); 530 } 531 532 public Iterable<Symbol> getSymbols(final Filter<Symbol> sf, 533 final LookupKind lookupKind) { 534 return new Iterable<Symbol>() { 535 public Iterator<Symbol> iterator() { 536 return new Iterator<Symbol>() { 537 private ScopeImpl currScope = ScopeImpl.this; 538 private Scope.Entry currEntry = elems; 539 { 540 update(); 541 } 542 543 public boolean hasNext() { 544 return currEntry != null; 545 } 546 547 public Symbol next() { 548 Symbol sym = (currEntry == null ? null : currEntry.sym); 549 if (currEntry != null) { 550 currEntry = currEntry.sibling; 551 } 552 update(); 553 return sym; 554 } 555 556 public void remove() { 557 throw new UnsupportedOperationException(); 558 } 559 560 private void update() { 561 skipToNextMatchingEntry(); 562 if (lookupKind == RECURSIVE) { 563 while (currEntry == null && currScope.next != null) { 564 currScope = currScope.next; 565 currEntry = currScope.elems; 566 skipToNextMatchingEntry(); 567 } 568 } 569 } 570 571 void skipToNextMatchingEntry() { 572 while (currEntry != null && sf != null && !sf.accepts(currEntry.sym)) { 573 currEntry = currEntry.sibling; 574 } 575 } 576 }; 577 } 578 }; 579 } 580 581 public Iterable<Symbol> getSymbolsByName(final Name name, 582 final Filter<Symbol> sf, 583 final LookupKind lookupKind) { 584 return new Iterable<Symbol>() { 585 public Iterator<Symbol> iterator() { 586 return new Iterator<Symbol>() { 587 Scope.Entry currentEntry = lookup(name, sf); 588 589 public boolean hasNext() { 590 return currentEntry.scope != null && 591 (lookupKind == RECURSIVE || 592 currentEntry.scope == ScopeImpl.this); 593 } 594 public Symbol next() { 595 Scope.Entry prevEntry = currentEntry; 596 currentEntry = currentEntry.next(sf); 597 return prevEntry.sym; 598 } 599 public void remove() { 600 throw new UnsupportedOperationException(); 601 } 602 }; 603 } 604 }; 605 } 606 607 public Scope getOrigin(Symbol s) { 608 for (Scope.Entry e = lookup(s.name); e.scope != null ; e = e.next()) { 609 if (e.sym == s) { 610 return this; 611 } 612 } 613 return null; 614 } 615 616 @Override 617 public boolean isStaticallyImported(Symbol s) { 618 return false; 619 } 620 621 public String toString() { 622 StringBuilder result = new StringBuilder(); 623 result.append("Scope["); 624 for (ScopeImpl s = this; s != null ; s = s.next) { 625 if (s != this) result.append(" | "); 626 for (Entry e = s.elems; e != null; e = e.sibling) { 627 if (e != s.elems) result.append(", "); 628 result.append(e.sym); 629 } 630 } 631 result.append("]"); 632 return result.toString(); 633 } 634 } 635 636 /** A class for scope entries. 637 */ 638 private static class Entry { 639 640 /** The referenced symbol. 641 * sym == null iff this == sentinel 642 */ 643 public Symbol sym; 644 645 /** An entry with the same hash code, or sentinel. 646 */ 647 private Entry shadowed; 648 649 /** Next entry in same scope. 650 */ 651 public Entry sibling; 652 653 /** The entry's scope. 654 * scope == null iff this == sentinel 655 */ 656 public Scope scope; 657 658 public Entry(Symbol sym, Entry shadowed, Entry sibling, Scope scope) { 659 this.sym = sym; 660 this.shadowed = shadowed; 661 this.sibling = sibling; 662 this.scope = scope; 663 } 664 665 /** Return next entry with the same name as this entry, proceeding 666 * outwards if not found in this scope. 667 */ 668 public Entry next() { 669 return shadowed; 670 } 671 672 public Entry next(Filter<Symbol> sf) { 673 if (shadowed.sym == null || sf == null || sf.accepts(shadowed.sym)) return shadowed; 674 else return shadowed.next(sf); 675 } 676 677 } 678 679 public static class ImportScope extends CompoundScope { 680 681 public ImportScope(Symbol owner) { 682 super(owner); 683 } 684 685 /**Finalize the content of the ImportScope to speed-up future lookups. 686 * No further changes to class hierarchy or class content will be reflected. 687 */ 688 public void finalizeScope() { 689 for (List<Scope> scopes = this.subScopes; scopes.nonEmpty(); scopes = scopes.tail) { 690 Scope impScope = scopes.head; 691 692 if (impScope instanceof FilterImportScope && impScope.owner.kind == Kind.TYP) { 693 WriteableScope finalized = WriteableScope.create(impScope.owner); 694 695 for (Symbol sym : impScope.getSymbols()) { 696 finalized.enter(sym); 697 } 698 699 finalized.addScopeListener(new ScopeListener() { 700 @Override 701 public void symbolAdded(Symbol sym, Scope s) { 702 Assert.error("The scope is sealed."); 703 } 704 @Override 705 public void symbolRemoved(Symbol sym, Scope s) { 706 Assert.error("The scope is sealed."); 707 } 708 }); 709 710 scopes.head = finalized; 711 } 712 } 713 } 714 715 } 716 717 public static class NamedImportScope extends ImportScope { 718 719 public NamedImportScope(Symbol owner, Scope currentFileScope) { 720 super(owner); 721 prependSubScope(currentFileScope); 722 } 723 724 public Scope importByName(Types types, Scope origin, Name name, ImportFilter filter) { 725 return appendScope(new FilterImportScope(types, origin, name, filter, true)); 726 } 727 728 public Scope importType(Scope delegate, Scope origin, Symbol sym) { 729 return appendScope(new SingleEntryScope(delegate.owner, sym, origin)); 730 } 731 732 private Scope appendScope(Scope newScope) { 733 List<Scope> existingScopes = this.subScopes.reverse(); 734 subScopes = List.of(existingScopes.head); 735 subScopes = subScopes.prepend(newScope); 736 for (Scope s : existingScopes.tail) { 737 subScopes = subScopes.prepend(s); 738 } 739 return newScope; 740 } 741 742 private static class SingleEntryScope extends Scope { 743 744 private final Symbol sym; 745 private final List<Symbol> content; 746 private final Scope origin; 747 748 public SingleEntryScope(Symbol owner, Symbol sym, Scope origin) { 749 super(owner); 750 this.sym = sym; 751 this.content = List.of(sym); 752 this.origin = origin; 753 } 754 755 @Override 756 public Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind) { 757 return sf == null || sf.accepts(sym) ? content : Collections.<Symbol>emptyList(); 758 } 759 760 @Override 761 public Iterable<Symbol> getSymbolsByName(Name name, 762 Filter<Symbol> sf, 763 LookupKind lookupKind) { 764 return sym.name == name && 765 (sf == null || sf.accepts(sym)) ? content : Collections.<Symbol>emptyList(); 766 } 767 768 @Override 769 public Scope getOrigin(Symbol byName) { 770 return sym == byName ? origin : null; 771 } 772 773 @Override 774 public boolean isStaticallyImported(Symbol byName) { 775 return false; 776 } 777 778 } 779 } 780 781 public static class StarImportScope extends ImportScope { 782 783 public StarImportScope(Symbol owner) { 784 super(owner); 785 } 786 787 public void importAll(Types types, Scope origin, 788 ImportFilter filter, 789 boolean staticImport) { 790 for (Scope existing : subScopes) { 791 Assert.check(existing instanceof FilterImportScope); 792 FilterImportScope fis = (FilterImportScope) existing; 793 if (fis.origin == origin && fis.filter == filter && 794 fis.staticImport == staticImport) 795 return ; //avoid entering the same scope twice 796 } 797 prependSubScope(new FilterImportScope(types, origin, null, filter, staticImport)); 798 } 799 800 public boolean isFilled() { 801 return subScopes.nonEmpty(); 802 } 803 804 } 805 806 public interface ImportFilter { 807 public boolean accepts(Scope origin, Symbol sym); 808 } 809 810 private static class FilterImportScope extends Scope { 811 812 private final Types types; 813 private final Scope origin; 814 private final Name filterName; 815 private final ImportFilter filter; 816 private final boolean staticImport; 817 818 public FilterImportScope(Types types, 819 Scope origin, 820 Name filterName, 821 ImportFilter filter, 822 boolean staticImport) { 823 super(origin.owner); 824 this.types = types; 825 this.origin = origin; 826 this.filterName = filterName; 827 this.filter = filter; 828 this.staticImport = staticImport; 829 } 830 831 @Override 832 public Iterable<Symbol> getSymbols(final Filter<Symbol> sf, final LookupKind lookupKind) { 833 if (filterName != null) 834 return getSymbolsByName(filterName, sf, lookupKind); 835 SymbolImporter si = new SymbolImporter(staticImport) { 836 @Override 837 Iterable<Symbol> doLookup(TypeSymbol tsym) { 838 return tsym.members().getSymbols(sf, lookupKind); 839 } 840 }; 841 return si.importFrom((TypeSymbol) origin.owner) :: iterator; 842 } 843 844 @Override 845 public Iterable<Symbol> getSymbolsByName(final Name name, 846 final Filter<Symbol> sf, 847 final LookupKind lookupKind) { 848 if (filterName != null && filterName != name) 849 return Collections.emptyList(); 850 SymbolImporter si = new SymbolImporter(staticImport) { 851 @Override 852 Iterable<Symbol> doLookup(TypeSymbol tsym) { 853 return tsym.members().getSymbolsByName(name, sf, lookupKind); 854 } 855 }; 856 return si.importFrom((TypeSymbol) origin.owner) :: iterator; 857 } 858 859 @Override 860 public Scope getOrigin(Symbol byName) { 861 return origin; 862 } 863 864 @Override 865 public boolean isStaticallyImported(Symbol byName) { 866 return staticImport; 867 } 868 869 abstract class SymbolImporter { 870 Set<Symbol> processed = new HashSet<>(); 871 List<Iterable<Symbol>> delegates = List.nil(); 872 final boolean inspectSuperTypes; 873 public SymbolImporter(boolean inspectSuperTypes) { 874 this.inspectSuperTypes = inspectSuperTypes; 875 } 876 Stream<Symbol> importFrom(TypeSymbol tsym) { 877 if (tsym == null || !processed.add(tsym)) 878 return Stream.empty(); 879 880 Stream<Symbol> result = Stream.empty(); 881 882 if (inspectSuperTypes) { 883 // also import inherited names 884 result = importFrom(types.supertype(tsym.type).tsym); 885 for (Type t : types.interfaces(tsym.type)) 886 result = Stream.concat(importFrom(t.tsym), result); 887 } 888 889 return Stream.concat(StreamSupport.stream(doLookup(tsym).spliterator(), false) 890 .filter(s -> filter.accepts(origin, s)), 891 result); 892 } 893 abstract Iterable<Symbol> doLookup(TypeSymbol tsym); 894 } 895 896 } 897 898 /** A class scope adds capabilities to keep track of changes in related 899 * class scopes - this allows client to realize whether a class scope 900 * has changed, either directly (because a new member has been added/removed 901 * to this scope) or indirectly (i.e. because a new member has been 902 * added/removed into a supertype scope) 903 */ 904 public static class CompoundScope extends Scope implements ScopeListener { 905 906 List<Scope> subScopes = List.nil(); 907 private int mark = 0; 908 909 public CompoundScope(Symbol owner) { 910 super(owner); 911 } 912 913 public void prependSubScope(Scope that) { 914 if (that != null) { 915 subScopes = subScopes.prepend(that); 916 that.addScopeListener(this); 917 mark++; 918 for (ScopeListener sl : listeners) { 919 sl.symbolAdded(null, this); //propagate upwards in case of nested CompoundScopes 920 } 921 } 922 } 923 924 public void symbolAdded(Symbol sym, Scope s) { 925 mark++; 926 for (ScopeListener sl : listeners) { 927 sl.symbolAdded(sym, s); 928 } 929 } 930 931 public void symbolRemoved(Symbol sym, Scope s) { 932 mark++; 933 for (ScopeListener sl : listeners) { 934 sl.symbolRemoved(sym, s); 935 } 936 } 937 938 public int getMark() { 939 return mark; 940 } 941 942 @Override 943 public String toString() { 944 StringBuilder buf = new StringBuilder(); 945 buf.append("CompoundScope{"); 946 String sep = ""; 947 for (Scope s : subScopes) { 948 buf.append(sep); 949 buf.append(s); 950 sep = ","; 951 } 952 buf.append("}"); 953 return buf.toString(); 954 } 955 956 @Override 957 public Iterable<Symbol> getSymbols(final Filter<Symbol> sf, 958 final LookupKind lookupKind) { 959 return new Iterable<Symbol>() { 960 public Iterator<Symbol> iterator() { 961 return new CompoundScopeIterator(subScopes) { 962 Iterator<Symbol> nextIterator(Scope s) { 963 return s.getSymbols(sf, lookupKind).iterator(); 964 } 965 }; 966 } 967 }; 968 } 969 970 @Override 971 public Iterable<Symbol> getSymbolsByName(final Name name, 972 final Filter<Symbol> sf, 973 final LookupKind lookupKind) { 974 return new Iterable<Symbol>() { 975 public Iterator<Symbol> iterator() { 976 return new CompoundScopeIterator(subScopes) { 977 Iterator<Symbol> nextIterator(Scope s) { 978 return s.getSymbolsByName(name, sf, lookupKind).iterator(); 979 } 980 }; 981 } 982 }; 983 } 984 985 @Override 986 public Scope getOrigin(Symbol sym) { 987 for (Scope delegate : subScopes) { 988 if (delegate.includes(sym)) 989 return delegate.getOrigin(sym); 990 } 991 992 return null; 993 } 994 995 @Override 996 public boolean isStaticallyImported(Symbol sym) { 997 for (Scope delegate : subScopes) { 998 if (delegate.includes(sym)) 999 return delegate.isStaticallyImported(sym); 1000 } 1001 1002 return false; 1003 } 1004 1005 abstract class CompoundScopeIterator implements Iterator<Symbol> { 1006 1007 private Iterator<Symbol> currentIterator; 1008 private List<Scope> scopesToScan; 1009 1010 public CompoundScopeIterator(List<Scope> scopesToScan) { 1011 this.scopesToScan = scopesToScan; 1012 update(); 1013 } 1014 1015 abstract Iterator<Symbol> nextIterator(Scope s); 1016 1017 public boolean hasNext() { 1018 return currentIterator != null; 1019 } 1020 1021 public Symbol next() { 1022 Symbol sym = currentIterator.next(); 1023 if (!currentIterator.hasNext()) { 1024 update(); 1025 } 1026 return sym; 1027 } 1028 1029 public void remove() { 1030 throw new UnsupportedOperationException(); 1031 } 1032 1033 private void update() { 1034 while (scopesToScan.nonEmpty()) { 1035 currentIterator = nextIterator(scopesToScan.head); 1036 scopesToScan = scopesToScan.tail; 1037 if (currentIterator.hasNext()) return; 1038 } 1039 currentIterator = null; 1040 } 1041 } 1042 } 1043 1044 /** An error scope, for which the owner should be an error symbol. */ 1045 public static class ErrorScope extends ScopeImpl { 1046 ErrorScope(ScopeImpl next, Symbol errSymbol, Entry[] table) { 1047 super(next, /*owner=*/errSymbol, table); 1048 } 1049 public ErrorScope(Symbol errSymbol) { 1050 super(errSymbol); 1051 } 1052 public WriteableScope dup(Symbol newOwner) { 1053 return new ErrorScope(this, newOwner, table); 1054 } 1055 public WriteableScope dupUnshared(Symbol newOwner) { 1056 return new ErrorScope(this, newOwner, table.clone()); 1057 } 1058 public Entry lookup(Name name) { 1059 Entry e = super.lookup(name); 1060 if (e.scope == null) 1061 return new Entry(owner, null, null, null); 1062 else 1063 return e; 1064 } 1065 } 1066 }