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