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 
  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.  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         public boolean isFilled() {
 802             return subScopes.nonEmpty();
 803         }
 804 
 805     }
 806 
 807     public interface ImportFilter {
 808         public boolean accepts(Scope origin, Symbol sym);
 809     }
 810 
 811     private static class FilterImportScope extends Scope {
 812 
 813         private final Types types;
 814         private final Scope origin;
 815         private final Name  filterName;
 816         private final ImportFilter filter;
 817         private final boolean staticImport;
 818 
 819         public FilterImportScope(Types types,
 820                                  Scope origin,
 821                                  Name  filterName,
 822                                  ImportFilter filter,
 823                                  boolean staticImport) {
 824             super(origin.owner);
 825             this.types = types;
 826             this.origin = origin;
 827             this.filterName = filterName;
 828             this.filter = filter;
 829             this.staticImport = staticImport;
 830         }
 831 
 832         @Override
 833         public Iterable<Symbol> getSymbols(final Filter<Symbol> sf, final LookupKind lookupKind) {
 834             if (filterName != null)
 835                 return getSymbolsByName(filterName, sf, lookupKind);
 836             SymbolImporter si = new SymbolImporter(staticImport) {
 837                 @Override
 838                 Iterable<Symbol> doLookup(TypeSymbol tsym) {
 839                     return tsym.members().getSymbols(sf, lookupKind);
 840                 }
 841             };
 842             return si.importFrom((TypeSymbol) origin.owner) :: iterator;
 843         }
 844 
 845         @Override
 846         public Iterable<Symbol> getSymbolsByName(final Name name,
 847                                                  final Filter<Symbol> sf,
 848                                                  final LookupKind lookupKind) {
 849             if (filterName != null && filterName != name)
 850                 return Collections.emptyList();
 851             SymbolImporter si = new SymbolImporter(staticImport) {
 852                 @Override
 853                 Iterable<Symbol> doLookup(TypeSymbol tsym) {
 854                     return tsym.members().getSymbolsByName(name, sf, lookupKind);
 855                 }
 856             };
 857             return si.importFrom((TypeSymbol) origin.owner) :: iterator;
 858         }
 859 
 860         @Override
 861         public Scope getOrigin(Symbol byName) {
 862             return origin;
 863         }
 864 
 865         @Override
 866         public boolean isStaticallyImported(Symbol byName) {
 867             return staticImport;
 868         }
 869 
 870         abstract class SymbolImporter {
 871             Set<Symbol> processed = new HashSet<>();
 872             List<Iterable<Symbol>> delegates = List.nil();
 873             final boolean inspectSuperTypes;
 874             public SymbolImporter(boolean inspectSuperTypes) {
 875                 this.inspectSuperTypes = inspectSuperTypes;
 876             }
 877             Stream<Symbol> importFrom(TypeSymbol tsym) {
 878                 if (tsym == null || !processed.add(tsym))
 879                     return Stream.empty();
 880 
 881                 Stream<Symbol> result = Stream.empty();
 882 
 883                 if (inspectSuperTypes) {
 884                     // also import inherited names
 885                     result = importFrom(types.supertype(tsym.type).tsym);
 886                     for (Type t : types.interfaces(tsym.type))
 887                         result = Stream.concat(importFrom(t.tsym), result);
 888                 }
 889 
 890                 return Stream.concat(StreamSupport.stream(doLookup(tsym).spliterator(), false)
 891                                                   .filter(s -> filter.accepts(origin, s)),
 892                                      result);
 893             }
 894             abstract Iterable<Symbol> doLookup(TypeSymbol tsym);
 895         }
 896 
 897     }
 898 
 899     /** A class scope adds capabilities to keep track of changes in related
 900      *  class scopes - this allows client to realize whether a class scope
 901      *  has changed, either directly (because a new member has been added/removed
 902      *  to this scope) or indirectly (i.e. because a new member has been
 903      *  added/removed into a supertype scope)
 904      */
 905     public static class CompoundScope extends Scope implements ScopeListener {
 906 
 907         List<Scope> subScopes = List.nil();
 908         private int mark = 0;
 909 
 910         public CompoundScope(Symbol owner) {
 911             super(owner);
 912         }
 913 
 914         public void prependSubScope(Scope that) {
 915            if (that != null) {
 916                 subScopes = subScopes.prepend(that);
 917                 that.addScopeListener(this);
 918                 mark++;
 919                 for (ScopeListener sl : listeners) {
 920                     sl.symbolAdded(null, this); //propagate upwards in case of nested CompoundScopes
 921                 }
 922            }
 923         }
 924 
 925         public void symbolAdded(Symbol sym, Scope s) {
 926             mark++;
 927             for (ScopeListener sl : listeners) {
 928                 sl.symbolAdded(sym, s);
 929             }
 930         }
 931 
 932         public void symbolRemoved(Symbol sym, Scope s) {
 933             mark++;
 934             for (ScopeListener sl : listeners) {
 935                 sl.symbolRemoved(sym, s);
 936             }
 937         }
 938 
 939         public int getMark() {
 940             return mark;
 941         }
 942 
 943         @Override
 944         public String toString() {
 945             StringBuilder buf = new StringBuilder();
 946             buf.append("CompoundScope{");
 947             String sep = "";
 948             for (Scope s : subScopes) {
 949                 buf.append(sep);
 950                 buf.append(s);
 951                 sep = ",";
 952             }
 953             buf.append("}");
 954             return buf.toString();
 955         }
 956 
 957         @Override
 958         public Iterable<Symbol> getSymbols(final Filter<Symbol> sf,
 959                                            final LookupKind lookupKind) {
 960             return () -> Iterators.createCompoundIterator(subScopes,
 961                                                           scope -> scope.getSymbols(sf,
 962                                                                                     lookupKind)
 963                                                                         .iterator());
 964         }
 965 
 966         @Override
 967         public Iterable<Symbol> getSymbolsByName(final Name name,
 968                                                  final Filter<Symbol> sf,
 969                                                  final LookupKind lookupKind) {
 970             return () -> Iterators.createCompoundIterator(subScopes,
 971                                                           scope -> scope.getSymbolsByName(name,
 972                                                                                           sf,
 973                                                                                           lookupKind)
 974                                                                         .iterator());
 975         }
 976 
 977         @Override
 978         public Scope getOrigin(Symbol sym) {
 979             for (Scope delegate : subScopes) {
 980                 if (delegate.includes(sym))
 981                     return delegate.getOrigin(sym);
 982             }
 983 
 984             return null;
 985         }
 986 
 987         @Override
 988         public boolean isStaticallyImported(Symbol sym) {
 989             for (Scope delegate : subScopes) {
 990                 if (delegate.includes(sym))
 991                     return delegate.isStaticallyImported(sym);
 992             }
 993 
 994             return false;
 995         }
 996 
 997     }
 998 
 999     /** An error scope, for which the owner should be an error symbol. */
1000     public static class ErrorScope extends ScopeImpl {
1001         ErrorScope(ScopeImpl next, Symbol errSymbol, Entry[] table) {
1002             super(next, /*owner=*/errSymbol, table);
1003         }
1004         public ErrorScope(Symbol errSymbol) {
1005             super(errSymbol);
1006         }
1007         public WriteableScope dup(Symbol newOwner) {
1008             return new ErrorScope(this, newOwner, table);
1009         }
1010         public WriteableScope dupUnshared(Symbol newOwner) {
1011             return new ErrorScope(this, newOwner, table.clone());
1012         }
1013         public Entry lookup(Name name) {
1014             Entry e = super.lookup(name);
1015             if (e.scope == null)
1016                 return new Entry(owner, null, null, null);
1017             else
1018                 return e;
1019         }
1020     }
1021 }