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