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