Scope.java revision 2947:283c9951fd23
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
26package com.sun.tools.javac.code;
27
28import com.sun.tools.javac.code.Kinds.Kind;
29import java.util.*;
30
31import com.sun.tools.javac.code.Symbol.TypeSymbol;
32import com.sun.tools.javac.tree.JCTree.JCImport;
33import com.sun.tools.javac.util.*;
34import com.sun.tools.javac.util.List;
35
36import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
37import static com.sun.tools.javac.code.Scope.LookupKind.RECURSIVE;
38import java.util.stream.Stream;
39import java.util.stream.StreamSupport;
40
41/** A scope represents an area of visibility in a Java program. The
42 *  Scope class is a container for symbols which provides
43 *  efficient access to symbols given their names. Scopes are implemented
44 *  as hash tables with "open addressing" and "double hashing".
45 *  Scopes can be nested. Nested scopes can share their hash tables.
46 *
47 *  <p><b>This is NOT part of any supported API.
48 *  If you write code that depends on this, you do so at your own risk.
49 *  This code and its internal interfaces are subject to change or
50 *  deletion without notice.</b>
51 */
52public abstract class Scope {
53
54    /** The scope's owner.
55     */
56    public final Symbol owner;
57
58    protected Scope(Symbol owner) {
59        this.owner = owner;
60    }
61
62    /**Returns all Symbols in this Scope. Symbols from outward Scopes are included.
63     */
64    public final Iterable<Symbol> getSymbols() {
65        return getSymbols(noFilter);
66    }
67
68    /**Returns Symbols that match the given filter. Symbols from outward Scopes are included.
69     */
70    public final Iterable<Symbol> getSymbols(Filter<Symbol> sf) {
71        return getSymbols(sf, RECURSIVE);
72    }
73
74    /**Returns all Symbols in this Scope. Symbols from outward Scopes are included
75     * iff lookupKind == RECURSIVE.
76     */
77    public final Iterable<Symbol> getSymbols(LookupKind lookupKind) {
78        return getSymbols(noFilter, lookupKind);
79    }
80
81    /**Returns Symbols that match the given filter. Symbols from outward Scopes are included
82     * iff lookupKind == RECURSIVE.
83     */
84    public abstract Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind);
85
86    /**Returns Symbols with the given name. Symbols from outward Scopes are included.
87     */
88    public final Iterable<Symbol> getSymbolsByName(Name name) {
89        return getSymbolsByName(name, RECURSIVE);
90    }
91
92    /**Returns Symbols with the given name that match the given filter.
93     * Symbols from outward Scopes are included.
94     */
95    public final Iterable<Symbol> getSymbolsByName(final Name name, final Filter<Symbol> sf) {
96        return getSymbolsByName(name, sf, RECURSIVE);
97    }
98
99    /**Returns Symbols with the given name. Symbols from outward Scopes are included
100     * iff lookupKind == RECURSIVE.
101     */
102    public final Iterable<Symbol> getSymbolsByName(Name name, LookupKind lookupKind) {
103        return getSymbolsByName(name, noFilter, lookupKind);
104    }
105
106    /**Returns Symbols with the given name that match the given filter.
107     * Symbols from outward Scopes are included iff lookupKind == RECURSIVE.
108     */
109    public abstract Iterable<Symbol> getSymbolsByName(final Name name, final Filter<Symbol> sf,
110            final LookupKind lookupKind);
111
112    /** Return the first Symbol from this or outward scopes with the given name.
113     * Returns null if none.
114     */
115    public final Symbol findFirst(Name name) {
116        return findFirst(name, noFilter);
117    }
118
119    /** Return the first Symbol from this or outward scopes with the given name that matches the
120     *  given filter. Returns null if none.
121     */
122    public Symbol findFirst(Name name, Filter<Symbol> sf) {
123        Iterator<Symbol> it = getSymbolsByName(name, sf).iterator();
124        return it.hasNext() ? it.next() : null;
125    }
126
127    /** Returns true iff there are is at least one Symbol in this scope matching the given filter.
128     *  Does not inspect outward scopes.
129     */
130    public boolean anyMatch(Filter<Symbol> filter) {
131        return getSymbols(filter, NON_RECURSIVE).iterator().hasNext();
132    }
133
134    /** Returns true iff the given Symbol is in this scope or any outward scope.
135     */
136    public boolean includes(final Symbol sym) {
137        return getSymbolsByName(sym.name, new Filter<Symbol>() {
138            @Override
139            public boolean accepts(Symbol t) {
140                return t == sym;
141            }
142        }).iterator().hasNext();
143    }
144
145    /** Returns true iff this scope does not contain any Symbol. Does not inspect outward scopes.
146     */
147    public boolean isEmpty() {
148        return !getSymbols(NON_RECURSIVE).iterator().hasNext();
149    }
150
151    /** Returns the Scope from which the givins Symbol originates in this scope.
152     */
153    public abstract Scope getOrigin(Symbol byName);
154
155    /** Returns true iff the given Symbol is part of this scope due to a static import.
156     */
157    public abstract boolean isStaticallyImported(Symbol byName);
158
159    private static final Filter<Symbol> noFilter = null;
160
161    /** A list of scopes to be notified if items are to be removed from this scope.
162     */
163    List<ScopeListener> listeners = List.nil();
164
165    public void addScopeListener(ScopeListener sl) {
166        listeners = listeners.prepend(sl);
167    }
168
169    public interface ScopeListener {
170        public void symbolAdded(Symbol sym, Scope s);
171        public void symbolRemoved(Symbol sym, Scope s);
172    }
173
174    public enum LookupKind {
175        RECURSIVE,
176        NON_RECURSIVE;
177    }
178
179    /**A scope into which Symbols can be added.*/
180    public abstract static class WriteableScope extends Scope {
181
182        public WriteableScope(Symbol owner) {
183            super(owner);
184        }
185
186        /** Enter the given Symbol into this scope.
187         */
188        public abstract void enter(Symbol c);
189        /** Enter symbol sym in this scope if not already there.
190         */
191        public abstract void enterIfAbsent(Symbol c);
192
193        public abstract void remove(Symbol c);
194
195        /** Construct a fresh scope within this scope, with same owner. The new scope may
196         *  shares internal structures with the this scope. Used in connection with
197         *  method leave if scope access is stack-like in order to avoid allocation
198         *  of fresh tables.
199         */
200        public final WriteableScope dup() {
201            return dup(this.owner);
202        }
203
204        /** Construct a fresh scope within this scope, with new owner. The new scope may
205         *  shares internal structures with the this scope. Used in connection with
206         *  method leave if scope access is stack-like in order to avoid allocation
207         *  of fresh tables.
208         */
209        public abstract WriteableScope dup(Symbol newOwner);
210
211        /** Must be called on dup-ed scopes to be able to work with the outward scope again.
212         */
213        public abstract WriteableScope leave();
214
215        /** Construct a fresh scope within this scope, with same owner. The new scope
216         *  will not share internal structures with this scope.
217         */
218        public final WriteableScope dupUnshared() {
219            return dupUnshared(owner);
220        }
221
222        /** Construct a fresh scope within this scope, with new owner. The new scope
223         *  will not share internal structures with this scope.
224         */
225        public abstract WriteableScope dupUnshared(Symbol newOwner);
226
227        /** Create a new WriteableScope.
228         */
229        public static WriteableScope create(Symbol owner) {
230            return new ScopeImpl(owner);
231        }
232
233    }
234
235    private static class ScopeImpl extends WriteableScope {
236        /** The number of scopes that share this scope's hash table.
237         */
238        private int shared;
239
240        /** Next enclosing scope (with whom this scope may share a hashtable)
241         */
242        public ScopeImpl next;
243
244        /** A hash table for the scope's entries.
245         */
246        Entry[] table;
247
248        /** Mask for hash codes, always equal to (table.length - 1).
249         */
250        int hashMask;
251
252        /** A linear list that also contains all entries in
253         *  reverse order of appearance (i.e later entries are pushed on top).
254         */
255        public Entry elems;
256
257        /** The number of elements in this scope.
258         * This includes deleted elements, whose value is the sentinel.
259         */
260        int nelems = 0;
261
262        /** Use as a "not-found" result for lookup.
263         * Also used to mark deleted entries in the table.
264         */
265        private static final Entry sentinel = new Entry(null, null, null, null);
266
267        /** The hash table's initial size.
268         */
269        private static final int INITIAL_SIZE = 0x10;
270
271        /** Construct a new scope, within scope next, with given owner, using
272         *  given table. The table's length must be an exponent of 2.
273         */
274        private ScopeImpl(ScopeImpl next, Symbol owner, Entry[] table) {
275            super(owner);
276            this.next = next;
277            Assert.check(owner != null);
278            this.table = table;
279            this.hashMask = table.length - 1;
280        }
281
282        /** Convenience constructor used for dup and dupUnshared. */
283        private ScopeImpl(ScopeImpl next, Symbol owner, Entry[] table, int nelems) {
284            this(next, owner, table);
285            this.nelems = nelems;
286        }
287
288        /** Construct a new scope, within scope next, with given owner,
289         *  using a fresh table of length INITIAL_SIZE.
290         */
291        public ScopeImpl(Symbol owner) {
292            this(null, owner, new Entry[INITIAL_SIZE]);
293        }
294
295        /** Construct a fresh scope within this scope, with new owner,
296         *  which shares its table with the outer scope. Used in connection with
297         *  method leave if scope access is stack-like in order to avoid allocation
298         *  of fresh tables.
299         */
300        public WriteableScope dup(Symbol newOwner) {
301            ScopeImpl result = new ScopeImpl(this, newOwner, this.table, this.nelems);
302            shared++;
303            // System.out.println("====> duping scope " + this.hashCode() + " owned by " + newOwner + " to " + result.hashCode());
304            // new Error().printStackTrace(System.out);
305            return result;
306        }
307
308        /** Construct a fresh scope within this scope, with new owner,
309         *  with a new hash table, whose contents initially are those of
310         *  the table of its outer scope.
311         */
312        public WriteableScope dupUnshared(Symbol newOwner) {
313            if (shared > 0) {
314                //The nested Scopes might have already added something to the table, so all items
315                //that don't originate in this Scope or any of its outer Scopes need to be cleared:
316                Set<Scope> acceptScopes = Collections.newSetFromMap(new IdentityHashMap<>());
317                ScopeImpl c = this;
318                while (c != null) {
319                    acceptScopes.add(c);
320                    c = c.next;
321                }
322                int n = 0;
323                Entry[] oldTable = this.table;
324                Entry[] newTable = new Entry[this.table.length];
325                for (int i = 0; i < oldTable.length; i++) {
326                    Entry e = oldTable[i];
327                    while (e != null && e != sentinel && !acceptScopes.contains(e.scope)) {
328                        e = e.shadowed;
329                    }
330                    if (e != null) {
331                        n++;
332                        newTable[i] = e;
333                    }
334                }
335                return new ScopeImpl(this, newOwner, newTable, n);
336            } else {
337                return new ScopeImpl(this, newOwner, this.table.clone(), this.nelems);
338            }
339        }
340
341        /** Remove all entries of this scope from its table, if shared
342         *  with next.
343         */
344        public WriteableScope leave() {
345            Assert.check(shared == 0);
346            if (table != next.table) return next;
347            while (elems != null) {
348                int hash = getIndex(elems.sym.name);
349                Entry e = table[hash];
350                Assert.check(e == elems, elems.sym);
351                table[hash] = elems.shadowed;
352                elems = elems.sibling;
353            }
354            Assert.check(next.shared > 0);
355            next.shared--;
356            next.nelems = nelems;
357            // System.out.println("====> leaving scope " + this.hashCode() + " owned by " + this.owner + " to " + next.hashCode());
358            // new Error().printStackTrace(System.out);
359            return next;
360        }
361
362        /** Double size of hash table.
363         */
364        private void dble() {
365            Assert.check(shared == 0);
366            Entry[] oldtable = table;
367            Entry[] newtable = new Entry[oldtable.length * 2];
368            for (ScopeImpl s = this; s != null; s = s.next) {
369                if (s.table == oldtable) {
370                    Assert.check(s == this || s.shared != 0);
371                    s.table = newtable;
372                    s.hashMask = newtable.length - 1;
373                }
374            }
375            int n = 0;
376            for (int i = oldtable.length; --i >= 0; ) {
377                Entry e = oldtable[i];
378                if (e != null && e != sentinel) {
379                    table[getIndex(e.sym.name)] = e;
380                    n++;
381                }
382            }
383            // We don't need to update nelems for shared inherited scopes,
384            // since that gets handled by leave().
385            nelems = n;
386        }
387
388        /** Enter symbol sym in this scope.
389         */
390        public void enter(Symbol sym) {
391            Assert.check(shared == 0);
392            if (nelems * 3 >= hashMask * 2)
393                dble();
394            int hash = getIndex(sym.name);
395            Entry old = table[hash];
396            if (old == null) {
397                old = sentinel;
398                nelems++;
399            }
400            Entry e = new Entry(sym, old, elems, this);
401            table[hash] = e;
402            elems = e;
403
404            //notify listeners
405            for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) {
406                l.head.symbolAdded(sym, this);
407            }
408        }
409
410        /** Remove symbol from this scope.
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}
1067