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