Scope.java revision 2739:9d2192f36e53
1/* 2 * Copyright (c) 1999, 2014, 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.util.*; 30 31import com.sun.tools.javac.code.Symbol.TypeSymbol; 32import com.sun.tools.javac.tree.JCTree.JCImport; 33import com.sun.tools.javac.util.*; 34import com.sun.tools.javac.util.List; 35 36import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE; 37import static com.sun.tools.javac.code.Scope.LookupKind.RECURSIVE; 38import java.util.stream.Stream; 39import 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 */ 52public 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. Used when an inner class 411 * attribute tells us that the class isn't a package member. 412 */ 413 public void remove(Symbol sym) { 414 Assert.check(shared == 0); 415 Entry e = lookup(sym.name); 416 if (e.scope == null) return; 417 418 // remove e from table and shadowed list; 419 int i = getIndex(sym.name); 420 Entry te = table[i]; 421 if (te == e) 422 table[i] = e.shadowed; 423 else while (true) { 424 if (te.shadowed == e) { 425 te.shadowed = e.shadowed; 426 break; 427 } 428 te = te.shadowed; 429 } 430 431 // remove e from elems and sibling list 432 te = elems; 433 if (te == e) 434 elems = e.sibling; 435 else while (true) { 436 if (te.sibling == e) { 437 te.sibling = e.sibling; 438 break; 439 } 440 te = te.sibling; 441 } 442 443 //notify listeners 444 for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) { 445 l.head.symbolRemoved(sym, this); 446 } 447 } 448 449 /** Enter symbol sym in this scope if not already there. 450 */ 451 public void enterIfAbsent(Symbol sym) { 452 Assert.check(shared == 0); 453 Entry e = lookup(sym.name); 454 while (e.scope == this && e.sym.kind != sym.kind) e = e.next(); 455 if (e.scope != this) enter(sym); 456 } 457 458 /** Given a class, is there already a class with same fully 459 * qualified name in this (import) scope? 460 */ 461 public boolean includes(Symbol c) { 462 for (Scope.Entry e = lookup(c.name); 463 e.scope == this; 464 e = e.next()) { 465 if (e.sym == c) return true; 466 } 467 return false; 468 } 469 470 /** Return the entry associated with given name, starting in 471 * this scope and proceeding outwards. If no entry was found, 472 * return the sentinel, which is characterized by having a null in 473 * both its scope and sym fields, whereas both fields are non-null 474 * for regular entries. 475 */ 476 protected Entry lookup(Name name) { 477 return lookup(name, noFilter); 478 } 479 480 protected Entry lookup(Name name, Filter<Symbol> sf) { 481 Entry e = table[getIndex(name)]; 482 if (e == null || e == sentinel) 483 return sentinel; 484 while (e.scope != null && (e.sym.name != name || (sf != null && !sf.accepts(e.sym)))) 485 e = e.shadowed; 486 return e; 487 } 488 489 public Symbol findFirst(Name name, Filter<Symbol> sf) { 490 return lookup(name, sf).sym; 491 } 492 493 /*void dump (java.io.PrintStream out) { 494 out.println(this); 495 for (int l=0; l < table.length; l++) { 496 Entry le = table[l]; 497 out.print("#"+l+": "); 498 if (le==sentinel) out.println("sentinel"); 499 else if(le == null) out.println("null"); 500 else out.println(""+le+" s:"+le.sym); 501 } 502 }*/ 503 504 /** Look for slot in the table. 505 * We use open addressing with double hashing. 506 */ 507 int getIndex (Name name) { 508 int h = name.hashCode(); 509 int i = h & hashMask; 510 // The expression below is always odd, so it is guaranteed 511 // to be mutually prime with table.length, a power of 2. 512 int x = hashMask - ((h + (h >> 16)) << 1); 513 int d = -1; // Index of a deleted item. 514 for (;;) { 515 Entry e = table[i]; 516 if (e == null) 517 return d >= 0 ? d : i; 518 if (e == sentinel) { 519 // We have to keep searching even if we see a deleted item. 520 // However, remember the index in case we fail to find the name. 521 if (d < 0) 522 d = i; 523 } else if (e.sym.name == name) 524 return i; 525 i = (i + x) & hashMask; 526 } 527 } 528 529 public boolean anyMatch(Filter<Symbol> sf) { 530 return getSymbols(sf, NON_RECURSIVE).iterator().hasNext(); 531 } 532 533 public Iterable<Symbol> getSymbols(final Filter<Symbol> sf, 534 final LookupKind lookupKind) { 535 return new Iterable<Symbol>() { 536 public Iterator<Symbol> iterator() { 537 return new Iterator<Symbol>() { 538 private ScopeImpl currScope = ScopeImpl.this; 539 private Scope.Entry currEntry = elems; 540 { 541 update(); 542 } 543 544 public boolean hasNext() { 545 return currEntry != null; 546 } 547 548 public Symbol next() { 549 Symbol sym = (currEntry == null ? null : currEntry.sym); 550 if (currEntry != null) { 551 currEntry = currEntry.sibling; 552 } 553 update(); 554 return sym; 555 } 556 557 public void remove() { 558 throw new UnsupportedOperationException(); 559 } 560 561 private void update() { 562 skipToNextMatchingEntry(); 563 if (lookupKind == RECURSIVE) { 564 while (currEntry == null && currScope.next != null) { 565 currScope = currScope.next; 566 currEntry = currScope.elems; 567 skipToNextMatchingEntry(); 568 } 569 } 570 } 571 572 void skipToNextMatchingEntry() { 573 while (currEntry != null && sf != null && !sf.accepts(currEntry.sym)) { 574 currEntry = currEntry.sibling; 575 } 576 } 577 }; 578 } 579 }; 580 } 581 582 public Iterable<Symbol> getSymbolsByName(final Name name, 583 final Filter<Symbol> sf, 584 final LookupKind lookupKind) { 585 return new Iterable<Symbol>() { 586 public Iterator<Symbol> iterator() { 587 return new Iterator<Symbol>() { 588 Scope.Entry currentEntry = lookup(name, sf); 589 590 public boolean hasNext() { 591 return currentEntry.scope != null && 592 (lookupKind == RECURSIVE || 593 currentEntry.scope == ScopeImpl.this); 594 } 595 public Symbol next() { 596 Scope.Entry prevEntry = currentEntry; 597 currentEntry = currentEntry.next(sf); 598 return prevEntry.sym; 599 } 600 public void remove() { 601 throw new UnsupportedOperationException(); 602 } 603 }; 604 } 605 }; 606 } 607 608 public Scope getOrigin(Symbol s) { 609 for (Scope.Entry e = lookup(s.name); e.scope != null ; e = e.next()) { 610 if (e.sym == s) { 611 return this; 612 } 613 } 614 return null; 615 } 616 617 @Override 618 public boolean isStaticallyImported(Symbol s) { 619 return false; 620 } 621 622 public String toString() { 623 StringBuilder result = new StringBuilder(); 624 result.append("Scope["); 625 for (ScopeImpl s = this; s != null ; s = s.next) { 626 if (s != this) result.append(" | "); 627 for (Entry e = s.elems; e != null; e = e.sibling) { 628 if (e != s.elems) result.append(", "); 629 result.append(e.sym); 630 } 631 } 632 result.append("]"); 633 return result.toString(); 634 } 635 } 636 637 /** A class for scope entries. 638 */ 639 private static class Entry { 640 641 /** The referenced symbol. 642 * sym == null iff this == sentinel 643 */ 644 public Symbol sym; 645 646 /** An entry with the same hash code, or sentinel. 647 */ 648 private Entry shadowed; 649 650 /** Next entry in same scope. 651 */ 652 public Entry sibling; 653 654 /** The entry's scope. 655 * scope == null iff this == sentinel 656 */ 657 public Scope scope; 658 659 public Entry(Symbol sym, Entry shadowed, Entry sibling, Scope scope) { 660 this.sym = sym; 661 this.shadowed = shadowed; 662 this.sibling = sibling; 663 this.scope = scope; 664 } 665 666 /** Return next entry with the same name as this entry, proceeding 667 * outwards if not found in this scope. 668 */ 669 public Entry next() { 670 return shadowed; 671 } 672 673 public Entry next(Filter<Symbol> sf) { 674 if (shadowed.sym == null || sf == null || sf.accepts(shadowed.sym)) return shadowed; 675 else return shadowed.next(sf); 676 } 677 678 } 679 680 public static class ImportScope extends CompoundScope { 681 682 public ImportScope(Symbol owner) { 683 super(owner); 684 } 685 686 /**Finalize the content of the ImportScope to speed-up future lookups. 687 * No further changes to class hierarchy or class content will be reflected. 688 */ 689 public void finalizeScope() { 690 for (List<Scope> scopes = this.subScopes; scopes.nonEmpty(); scopes = scopes.tail) { 691 Scope impScope = scopes.head; 692 693 if (impScope instanceof FilterImportScope && impScope.owner.kind == Kind.TYP) { 694 WriteableScope finalized = WriteableScope.create(impScope.owner); 695 696 for (Symbol sym : impScope.getSymbols()) { 697 finalized.enter(sym); 698 } 699 700 finalized.addScopeListener(new ScopeListener() { 701 @Override 702 public void symbolAdded(Symbol sym, Scope s) { 703 Assert.error("The scope is sealed."); 704 } 705 @Override 706 public void symbolRemoved(Symbol sym, Scope s) { 707 Assert.error("The scope is sealed."); 708 } 709 }); 710 711 scopes.head = finalized; 712 } 713 } 714 } 715 716 } 717 718 public static class NamedImportScope extends ImportScope { 719 720 public NamedImportScope(Symbol owner, Scope currentFileScope) { 721 super(owner); 722 prependSubScope(currentFileScope); 723 } 724 725 public Scope importByName(Types types, Scope origin, Name name, ImportFilter filter) { 726 return appendScope(new FilterImportScope(types, origin, name, filter, true)); 727 } 728 729 public Scope importType(Scope delegate, Scope origin, Symbol sym) { 730 return appendScope(new SingleEntryScope(delegate.owner, sym, origin)); 731 } 732 733 private Scope appendScope(Scope newScope) { 734 List<Scope> existingScopes = this.subScopes.reverse(); 735 subScopes = List.of(existingScopes.head); 736 subScopes = subScopes.prepend(newScope); 737 for (Scope s : existingScopes.tail) { 738 subScopes = subScopes.prepend(s); 739 } 740 return newScope; 741 } 742 743 private static class SingleEntryScope extends Scope { 744 745 private final Symbol sym; 746 private final List<Symbol> content; 747 private final Scope origin; 748 749 public SingleEntryScope(Symbol owner, Symbol sym, Scope origin) { 750 super(owner); 751 this.sym = sym; 752 this.content = List.of(sym); 753 this.origin = origin; 754 } 755 756 @Override 757 public Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind) { 758 return sf == null || sf.accepts(sym) ? content : Collections.<Symbol>emptyList(); 759 } 760 761 @Override 762 public Iterable<Symbol> getSymbolsByName(Name name, 763 Filter<Symbol> sf, 764 LookupKind lookupKind) { 765 return sym.name == name && 766 (sf == null || sf.accepts(sym)) ? content : Collections.<Symbol>emptyList(); 767 } 768 769 @Override 770 public Scope getOrigin(Symbol byName) { 771 return sym == byName ? origin : null; 772 } 773 774 @Override 775 public boolean isStaticallyImported(Symbol byName) { 776 return false; 777 } 778 779 } 780 } 781 782 public static class StarImportScope extends ImportScope { 783 784 public StarImportScope(Symbol owner) { 785 super(owner); 786 } 787 788 public void importAll(Types types, Scope origin, 789 ImportFilter filter, 790 boolean staticImport) { 791 for (Scope existing : subScopes) { 792 Assert.check(existing instanceof FilterImportScope); 793 FilterImportScope fis = (FilterImportScope) existing; 794 if (fis.origin == origin && fis.filter == filter && 795 fis.staticImport == staticImport) 796 return ; //avoid entering the same scope twice 797 } 798 prependSubScope(new FilterImportScope(types, origin, null, filter, staticImport)); 799 } 800 801 } 802 803 public interface ImportFilter { 804 public boolean accepts(Scope origin, Symbol sym); 805 } 806 807 private static class FilterImportScope extends Scope { 808 809 private final Types types; 810 private final Scope origin; 811 private final Name filterName; 812 private final ImportFilter filter; 813 private final boolean staticImport; 814 815 public FilterImportScope(Types types, 816 Scope origin, 817 Name filterName, 818 ImportFilter filter, 819 boolean staticImport) { 820 super(origin.owner); 821 this.types = types; 822 this.origin = origin; 823 this.filterName = filterName; 824 this.filter = filter; 825 this.staticImport = staticImport; 826 } 827 828 @Override 829 public Iterable<Symbol> getSymbols(final Filter<Symbol> sf, final LookupKind lookupKind) { 830 if (filterName != null) 831 return getSymbolsByName(filterName, sf, lookupKind); 832 SymbolImporter si = new SymbolImporter(staticImport) { 833 @Override 834 Iterable<Symbol> doLookup(TypeSymbol tsym) { 835 return tsym.members().getSymbols(sf, lookupKind); 836 } 837 }; 838 return si.importFrom((TypeSymbol) origin.owner) :: iterator; 839 } 840 841 @Override 842 public Iterable<Symbol> getSymbolsByName(final Name name, 843 final Filter<Symbol> sf, 844 final LookupKind lookupKind) { 845 if (filterName != null && filterName != name) 846 return Collections.emptyList(); 847 SymbolImporter si = new SymbolImporter(staticImport) { 848 @Override 849 Iterable<Symbol> doLookup(TypeSymbol tsym) { 850 return tsym.members().getSymbolsByName(name, sf, lookupKind); 851 } 852 }; 853 return si.importFrom((TypeSymbol) origin.owner) :: iterator; 854 } 855 856 @Override 857 public Scope getOrigin(Symbol byName) { 858 return origin; 859 } 860 861 @Override 862 public boolean isStaticallyImported(Symbol byName) { 863 return staticImport; 864 } 865 866 abstract class SymbolImporter { 867 Set<Symbol> processed = new HashSet<>(); 868 List<Iterable<Symbol>> delegates = List.nil(); 869 final boolean inspectSuperTypes; 870 public SymbolImporter(boolean inspectSuperTypes) { 871 this.inspectSuperTypes = inspectSuperTypes; 872 } 873 Stream<Symbol> importFrom(TypeSymbol tsym) { 874 if (tsym == null || !processed.add(tsym)) 875 return Stream.empty(); 876 877 Stream<Symbol> result = Stream.empty(); 878 879 if (inspectSuperTypes) { 880 // also import inherited names 881 result = importFrom(types.supertype(tsym.type).tsym); 882 for (Type t : types.interfaces(tsym.type)) 883 result = Stream.concat(importFrom(t.tsym), result); 884 } 885 886 return Stream.concat(StreamSupport.stream(doLookup(tsym).spliterator(), false) 887 .filter(s -> filter.accepts(origin, s)), 888 result); 889 } 890 abstract Iterable<Symbol> doLookup(TypeSymbol tsym); 891 } 892 893 } 894 895 /** A class scope adds capabilities to keep track of changes in related 896 * class scopes - this allows client to realize whether a class scope 897 * has changed, either directly (because a new member has been added/removed 898 * to this scope) or indirectly (i.e. because a new member has been 899 * added/removed into a supertype scope) 900 */ 901 public static class CompoundScope extends Scope implements ScopeListener { 902 903 List<Scope> subScopes = List.nil(); 904 private int mark = 0; 905 906 public CompoundScope(Symbol owner) { 907 super(owner); 908 } 909 910 public void prependSubScope(Scope that) { 911 if (that != null) { 912 subScopes = subScopes.prepend(that); 913 that.addScopeListener(this); 914 mark++; 915 for (ScopeListener sl : listeners) { 916 sl.symbolAdded(null, this); //propagate upwards in case of nested CompoundScopes 917 } 918 } 919 } 920 921 public void symbolAdded(Symbol sym, Scope s) { 922 mark++; 923 for (ScopeListener sl : listeners) { 924 sl.symbolAdded(sym, s); 925 } 926 } 927 928 public void symbolRemoved(Symbol sym, Scope s) { 929 mark++; 930 for (ScopeListener sl : listeners) { 931 sl.symbolRemoved(sym, s); 932 } 933 } 934 935 public int getMark() { 936 return mark; 937 } 938 939 @Override 940 public String toString() { 941 StringBuilder buf = new StringBuilder(); 942 buf.append("CompoundScope{"); 943 String sep = ""; 944 for (Scope s : subScopes) { 945 buf.append(sep); 946 buf.append(s); 947 sep = ","; 948 } 949 buf.append("}"); 950 return buf.toString(); 951 } 952 953 @Override 954 public Iterable<Symbol> getSymbols(final Filter<Symbol> sf, 955 final LookupKind lookupKind) { 956 return new Iterable<Symbol>() { 957 public Iterator<Symbol> iterator() { 958 return new CompoundScopeIterator(subScopes) { 959 Iterator<Symbol> nextIterator(Scope s) { 960 return s.getSymbols(sf, lookupKind).iterator(); 961 } 962 }; 963 } 964 }; 965 } 966 967 @Override 968 public Iterable<Symbol> getSymbolsByName(final Name name, 969 final Filter<Symbol> sf, 970 final LookupKind lookupKind) { 971 return new Iterable<Symbol>() { 972 public Iterator<Symbol> iterator() { 973 return new CompoundScopeIterator(subScopes) { 974 Iterator<Symbol> nextIterator(Scope s) { 975 return s.getSymbolsByName(name, sf, lookupKind).iterator(); 976 } 977 }; 978 } 979 }; 980 } 981 982 @Override 983 public Scope getOrigin(Symbol sym) { 984 for (Scope delegate : subScopes) { 985 if (delegate.includes(sym)) 986 return delegate.getOrigin(sym); 987 } 988 989 return null; 990 } 991 992 @Override 993 public boolean isStaticallyImported(Symbol sym) { 994 for (Scope delegate : subScopes) { 995 if (delegate.includes(sym)) 996 return delegate.isStaticallyImported(sym); 997 } 998 999 return false; 1000 } 1001 1002 abstract class CompoundScopeIterator implements Iterator<Symbol> { 1003 1004 private Iterator<Symbol> currentIterator; 1005 private List<Scope> scopesToScan; 1006 1007 public CompoundScopeIterator(List<Scope> scopesToScan) { 1008 this.scopesToScan = scopesToScan; 1009 update(); 1010 } 1011 1012 abstract Iterator<Symbol> nextIterator(Scope s); 1013 1014 public boolean hasNext() { 1015 return currentIterator != null; 1016 } 1017 1018 public Symbol next() { 1019 Symbol sym = currentIterator.next(); 1020 if (!currentIterator.hasNext()) { 1021 update(); 1022 } 1023 return sym; 1024 } 1025 1026 public void remove() { 1027 throw new UnsupportedOperationException(); 1028 } 1029 1030 private void update() { 1031 while (scopesToScan.nonEmpty()) { 1032 currentIterator = nextIterator(scopesToScan.head); 1033 scopesToScan = scopesToScan.tail; 1034 if (currentIterator.hasNext()) return; 1035 } 1036 currentIterator = null; 1037 } 1038 } 1039 } 1040 1041 /** An error scope, for which the owner should be an error symbol. */ 1042 public static class ErrorScope extends ScopeImpl { 1043 ErrorScope(ScopeImpl next, Symbol errSymbol, Entry[] table) { 1044 super(next, /*owner=*/errSymbol, table); 1045 } 1046 public ErrorScope(Symbol errSymbol) { 1047 super(errSymbol); 1048 } 1049 public WriteableScope dup(Symbol newOwner) { 1050 return new ErrorScope(this, newOwner, table); 1051 } 1052 public WriteableScope dupUnshared(Symbol newOwner) { 1053 return new ErrorScope(this, newOwner, table.clone()); 1054 } 1055 public Entry lookup(Name name) { 1056 Entry e = super.lookup(name); 1057 if (e.scope == null) 1058 return new Entry(owner, null, null, null); 1059 else 1060 return e; 1061 } 1062 } 1063} 1064