Scope.java revision 3563:45241cff9d3a
156893Sfenner/*
256893Sfenner * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
356893Sfenner * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
456893Sfenner *
556893Sfenner * This code is free software; you can redistribute it and/or modify it
656893Sfenner * under the terms of the GNU General Public License version 2 only, as
756893Sfenner * published by the Free Software Foundation.  Oracle designates this
856893Sfenner * particular file as subject to the "Classpath" exception as provided
956893Sfenner * by Oracle in the LICENSE file that accompanied this code.
1056893Sfenner *
1156893Sfenner * This code is distributed in the hope that it will be useful, but WITHOUT
1256893Sfenner * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1356893Sfenner * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1456893Sfenner * version 2 for more details (a copy is included in the LICENSE file that
1556893Sfenner * accompanied this code).
1656893Sfenner *
1756893Sfenner * You should have received a copy of the GNU General Public License version
1856893Sfenner * 2 along with this work; if not, write to the Free Software Foundation,
1956893Sfenner * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2056893Sfenner *
2156893Sfenner * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2256893Sfenner * or visit www.oracle.com if you need additional information or have any
23127668Sbms * questions.
24147899Ssam */
2556893Sfenner
2656893Sfennerpackage com.sun.tools.javac.code;
2756893Sfenner
2856893Sfennerimport com.sun.tools.javac.code.Kinds.Kind;
2956893Sfennerimport java.lang.ref.WeakReference;
3056893Sfennerimport java.util.*;
3156893Sfennerimport java.util.function.BiConsumer;
3256893Sfennerimport java.util.stream.Stream;
33127668Sbmsimport java.util.stream.StreamSupport;
3456893Sfenner
3556893Sfennerimport com.sun.tools.javac.code.Symbol.CompletionFailure;
3698524Sfennerimport com.sun.tools.javac.code.Symbol.TypeSymbol;
3756893Sfennerimport com.sun.tools.javac.tree.JCTree.JCImport;
38147899Ssamimport com.sun.tools.javac.util.*;
39147899Ssamimport com.sun.tools.javac.util.List;
40147899Ssam
41147899Ssamimport static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
4275115Sfennerimport static com.sun.tools.javac.code.Scope.LookupKind.RECURSIVE;
4375115Sfenner
44127668Sbms/** A scope represents an area of visibility in a Java program. The
4556893Sfenner *  Scope class is a container for symbols which provides
4675115Sfenner *  efficient access to symbols given their names. Scopes are implemented
4775115Sfenner *  as hash tables with "open addressing" and "double hashing".
4875115Sfenner *  Scopes can be nested. Nested scopes can share their hash tables.
4998524Sfenner *
5098524Sfenner *  <p><b>This is NOT part of any supported API.
5198524Sfenner *  If you write code that depends on this, you do so at your own risk.
52127668Sbms *  This code and its internal interfaces are subject to change or
53127668Sbms *  deletion without notice.</b>
54146773Ssam */
55146773Ssampublic abstract class Scope {
56127668Sbms
5775115Sfenner    /** The scope's owner.
58127668Sbms     */
59127668Sbms    public final Symbol owner;
6056893Sfenner
6175115Sfenner    protected Scope(Symbol owner) {
6275115Sfenner        this.owner = owner;
6375115Sfenner    }
6475115Sfenner
65146773Ssam    /**Returns all Symbols in this Scope. Symbols from outward Scopes are included.
66146773Ssam     */
67146773Ssam    public final Iterable<Symbol> getSymbols() {
68146773Ssam        return getSymbols(noFilter);
69146773Ssam    }
70146773Ssam
71146773Ssam    /**Returns Symbols that match the given filter. Symbols from outward Scopes are included.
72147899Ssam     */
73147899Ssam    public final Iterable<Symbol> getSymbols(Filter<Symbol> sf) {
74147899Ssam        return getSymbols(sf, RECURSIVE);
75147899Ssam    }
76146773Ssam
77146773Ssam    /**Returns all Symbols in this Scope. Symbols from outward Scopes are included
78146773Ssam     * iff lookupKind == RECURSIVE.
79146773Ssam     */
80146773Ssam    public final Iterable<Symbol> getSymbols(LookupKind lookupKind) {
81146773Ssam        return getSymbols(noFilter, lookupKind);
82146773Ssam    }
83147899Ssam
84146773Ssam    /**Returns Symbols that match the given filter. Symbols from outward Scopes are included
85146773Ssam     * iff lookupKind == RECURSIVE.
86146773Ssam     */
87146773Ssam    public abstract Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind);
88146773Ssam
89146773Ssam    /**Returns Symbols with the given name. Symbols from outward Scopes are included.
90146773Ssam     */
91146773Ssam    public final Iterable<Symbol> getSymbolsByName(Name name) {
92146773Ssam        return getSymbolsByName(name, RECURSIVE);
93146773Ssam    }
94146773Ssam
95146773Ssam    /**Returns Symbols with the given name that match the given filter.
96146773Ssam     * Symbols from outward Scopes are included.
97146773Ssam     */
98146773Ssam    public final Iterable<Symbol> getSymbolsByName(final Name name, final Filter<Symbol> sf) {
99146773Ssam        return getSymbolsByName(name, sf, RECURSIVE);
100146773Ssam    }
101146773Ssam
102146773Ssam    /**Returns Symbols with the given name. Symbols from outward Scopes are included
103146773Ssam     * iff lookupKind == RECURSIVE.
104146773Ssam     */
105146773Ssam    public final Iterable<Symbol> getSymbolsByName(Name name, LookupKind lookupKind) {
106146773Ssam        return getSymbolsByName(name, noFilter, lookupKind);
107146773Ssam    }
108146773Ssam
109146773Ssam    /**Returns Symbols with the given name that match the given filter.
110146773Ssam     * Symbols from outward Scopes are included iff lookupKind == RECURSIVE.
111146773Ssam     */
112146773Ssam    public abstract Iterable<Symbol> getSymbolsByName(final Name name, final Filter<Symbol> sf,
113146773Ssam            final LookupKind lookupKind);
114146773Ssam
115146773Ssam    /** Return the first Symbol from this or outward scopes with the given name.
116146773Ssam     * Returns null if none.
117146773Ssam     */
118146773Ssam    public final Symbol findFirst(Name name) {
119146773Ssam        return findFirst(name, noFilter);
120146773Ssam    }
121146773Ssam
122146773Ssam    /** Return the first Symbol from this or outward scopes with the given name that matches the
123146773Ssam     *  given filter. Returns null if none.
124146773Ssam     */
125146773Ssam    public Symbol findFirst(Name name, Filter<Symbol> sf) {
126146773Ssam        Iterator<Symbol> it = getSymbolsByName(name, sf).iterator();
127146773Ssam        return it.hasNext() ? it.next() : null;
128146773Ssam    }
129146773Ssam
130146773Ssam    /** Returns true iff there are is at least one Symbol in this scope matching the given filter.
131146773Ssam     *  Does not inspect outward scopes.
132146773Ssam     */
133146773Ssam    public boolean anyMatch(Filter<Symbol> filter) {
134146773Ssam        return getSymbols(filter, NON_RECURSIVE).iterator().hasNext();
135146773Ssam    }
136146773Ssam
137146773Ssam    /** Returns true iff the given Symbol is in this scope or any outward scope.
138146773Ssam     */
139146773Ssam    public boolean includes(final Symbol sym) {
140146773Ssam        return getSymbolsByName(sym.name, new Filter<Symbol>() {
141146773Ssam            @Override
142146773Ssam            public boolean accepts(Symbol t) {
143146773Ssam                return t == sym;
144146773Ssam            }
145146773Ssam        }).iterator().hasNext();
146146773Ssam    }
147146773Ssam
148146773Ssam    /** Returns true iff this scope does not contain any Symbol. Does not inspect outward scopes.
149146773Ssam     */
150146773Ssam    public boolean isEmpty() {
15198524Sfenner        return !getSymbols(NON_RECURSIVE).iterator().hasNext();
15298524Sfenner    }
15398524Sfenner
15498524Sfenner    /** Returns the Scope from which the givins Symbol originates in this scope.
15598524Sfenner     */
15698524Sfenner    public abstract Scope getOrigin(Symbol byName);
15798524Sfenner
15898524Sfenner    /** Returns true iff the given Symbol is part of this scope due to a static import.
15998524Sfenner     */
16098524Sfenner    public abstract boolean isStaticallyImported(Symbol byName);
16198524Sfenner
16298524Sfenner    private static final Filter<Symbol> noFilter = null;
16398524Sfenner
16498524Sfenner    /** A list of scopes to be notified if items are to be removed from this scope.
16598524Sfenner     */
16698524Sfenner    ScopeListenerList listeners = new ScopeListenerList();
16798524Sfenner
16898524Sfenner    public interface ScopeListener {
16998524Sfenner        void symbolAdded(Symbol sym, Scope s);
17098524Sfenner        void symbolRemoved(Symbol sym, Scope s);
17198524Sfenner    }
17298524Sfenner
17398524Sfenner    /**
17498524Sfenner     * A list of scope listeners; listeners are stored in weak references, to avoid memory leaks.
17598524Sfenner     * When the listener list is scanned (upon notification), elements corresponding to GC-ed
17698524Sfenner     * listeners are removed so that the listener list size is kept in check.
17798524Sfenner     */
17898524Sfenner    public static class ScopeListenerList {
17998524Sfenner
18098524Sfenner        List<WeakReference<ScopeListener>> listeners = List.nil();
18198524Sfenner
18298524Sfenner        void add(ScopeListener sl) {
18398524Sfenner            listeners = listeners.prepend(new WeakReference<>(sl));
18498524Sfenner        }
18598524Sfenner
18698524Sfenner        void symbolAdded(Symbol sym, Scope scope) {
18798524Sfenner            walkReferences(sym, scope, false);
18898524Sfenner        }
18998524Sfenner
19098524Sfenner        void symbolRemoved(Symbol sym, Scope scope) {
19198524Sfenner            walkReferences(sym, scope, true);
192127668Sbms        }
193127668Sbms
194127668Sbms        private void walkReferences(Symbol sym, Scope scope, boolean isRemove) {
195127668Sbms            ListBuffer<WeakReference<ScopeListener>> newListeners = new ListBuffer<>();
196127668Sbms            for (WeakReference<ScopeListener> wsl : listeners) {
197127668Sbms                ScopeListener sl = wsl.get();
198127668Sbms                if (sl != null) {
199127668Sbms                    if (isRemove) {
200127668Sbms                        sl.symbolRemoved(sym, scope);
201127668Sbms                    } else {
202127668Sbms                        sl.symbolAdded(sym, scope);
203127668Sbms                    }
204127668Sbms                    newListeners.add(wsl);
205127668Sbms                }
206127668Sbms            }
207127668Sbms            listeners = newListeners.toList();
208127668Sbms        }
209127668Sbms    }
210127668Sbms
211127668Sbms    public enum LookupKind {
212127668Sbms        RECURSIVE,
213127668Sbms        NON_RECURSIVE;
214127668Sbms    }
215127668Sbms
216127668Sbms    /**A scope into which Symbols can be added.*/
217127668Sbms    public abstract static class WriteableScope extends Scope {
218127668Sbms
219127668Sbms        public WriteableScope(Symbol owner) {
220127668Sbms            super(owner);
221127668Sbms        }
222127668Sbms
223127668Sbms        /** Enter the given Symbol into this scope.
224127668Sbms         */
225127668Sbms        public abstract void enter(Symbol c);
226127668Sbms        /** Enter symbol sym in this scope if not already there.
227127668Sbms         */
228127668Sbms        public abstract void enterIfAbsent(Symbol c);
229127668Sbms
230127668Sbms        public abstract void remove(Symbol c);
231127668Sbms
232127668Sbms        /** Construct a fresh scope within this scope, with same owner. The new scope may
233127668Sbms         *  shares internal structures with the this scope. Used in connection with
234127668Sbms         *  method leave if scope access is stack-like in order to avoid allocation
23556893Sfenner         *  of fresh tables.
236127668Sbms         */
23756893Sfenner        public final WriteableScope dup() {
23875115Sfenner            return dup(this.owner);
23998524Sfenner        }
24098524Sfenner
24198524Sfenner        /** Construct a fresh scope within this scope, with new owner. The new scope may
24298524Sfenner         *  shares internal structures with the this scope. Used in connection with
24398524Sfenner         *  method leave if scope access is stack-like in order to avoid allocation
244127668Sbms         *  of fresh tables.
24556893Sfenner         */
24656893Sfenner        public abstract WriteableScope dup(Symbol newOwner);
24756893Sfenner
24856893Sfenner        /** Must be called on dup-ed scopes to be able to work with the outward scope again.
24975115Sfenner         */
25056893Sfenner        public abstract WriteableScope leave();
25156893Sfenner
252127668Sbms        /** Construct a fresh scope within this scope, with same owner. The new scope
253127668Sbms         *  will not share internal structures with this scope.
254127668Sbms         */
255127668Sbms        public final WriteableScope dupUnshared() {
256127668Sbms            return dupUnshared(owner);
257127668Sbms        }
258127668Sbms
259127668Sbms        /** Construct a fresh scope within this scope, with new owner. The new scope
260127668Sbms         *  will not share internal structures with this scope.
261127668Sbms         */
262127668Sbms        public abstract WriteableScope dupUnshared(Symbol newOwner);
263127668Sbms
264127668Sbms        /** Create a new WriteableScope.
265127668Sbms         */
266146773Ssam        public static WriteableScope create(Symbol owner) {
267146773Ssam            return new ScopeImpl(owner);
268146773Ssam        }
269146773Ssam
270146773Ssam    }
271146773Ssam
272146773Ssam    private static class ScopeImpl extends WriteableScope {
273146773Ssam        /** The number of scopes that share this scope's hash table.
274146773Ssam         */
275146773Ssam        private int shared;
276146773Ssam
277146773Ssam        /** Next enclosing scope (with whom this scope may share a hashtable)
278146773Ssam         */
27975115Sfenner        public ScopeImpl next;
28056893Sfenner
28156893Sfenner        /** A hash table for the scope's entries.
282146773Ssam         */
28356893Sfenner        Entry[] table;
284146773Ssam
285146773Ssam        /** Mask for hash codes, always equal to (table.length - 1).
28656893Sfenner         */
287146773Ssam        int hashMask;
288146773Ssam
289146773Ssam        /** A linear list that also contains all entries in
29056893Sfenner         *  reverse order of appearance (i.e later entries are pushed on top).
291146773Ssam         */
29256893Sfenner        public Entry elems;
29356893Sfenner
29456893Sfenner        /** The number of elements in this scope.
29556893Sfenner         * This includes deleted elements, whose value is the sentinel.
29675115Sfenner         */
29775115Sfenner        int nelems = 0;
29875115Sfenner
29975115Sfenner        int removeCount = 0;
300127668Sbms
30175115Sfenner        /** Use as a "not-found" result for lookup.
30256893Sfenner         * Also used to mark deleted entries in the table.
303146773Ssam         */
30456893Sfenner        private static final Entry sentinel = new Entry(null, null, null, null);
30556893Sfenner
30656893Sfenner        /** The hash table's initial size.
30756893Sfenner         */
308146773Ssam        private static final int INITIAL_SIZE = 0x10;
30956893Sfenner
31056893Sfenner        /** Construct a new scope, within scope next, with given owner, using
31156893Sfenner         *  given table. The table's length must be an exponent of 2.
31256893Sfenner         */
313146773Ssam        private ScopeImpl(ScopeImpl next, Symbol owner, Entry[] table) {
31456893Sfenner            super(owner);
31556893Sfenner            this.next = next;
31656893Sfenner            Assert.check(owner != null);
31756893Sfenner            this.table = table;
31856893Sfenner            this.hashMask = table.length - 1;
31956893Sfenner        }
320146773Ssam
321146773Ssam        /** Convenience constructor used for dup and dupUnshared. */
322146773Ssam        private ScopeImpl(ScopeImpl next, Symbol owner, Entry[] table, int nelems) {
323146773Ssam            this(next, owner, table);
324146773Ssam            this.nelems = nelems;
32556893Sfenner        }
32656893Sfenner
32756893Sfenner        /** Construct a new scope, within scope next, with given owner,
32856893Sfenner         *  using a fresh table of length INITIAL_SIZE.
329146773Ssam         */
33056893Sfenner        public ScopeImpl(Symbol owner) {
33156893Sfenner            this(null, owner, new Entry[INITIAL_SIZE]);
33256893Sfenner        }
33356893Sfenner
33456893Sfenner        /** Construct a fresh scope within this scope, with new owner,
335146773Ssam         *  which shares its table with the outer scope. Used in connection with
33656893Sfenner         *  method leave if scope access is stack-like in order to avoid allocation
33756893Sfenner         *  of fresh tables.
33856893Sfenner         */
339146773Ssam        public WriteableScope dup(Symbol newOwner) {
34056893Sfenner            ScopeImpl result = new ScopeImpl(this, newOwner, this.table, this.nelems);
34156893Sfenner            shared++;
342146773Ssam            // System.out.println("====> duping scope " + this.hashCode() + " owned by " + newOwner + " to " + result.hashCode());
34356893Sfenner            // new Error().printStackTrace(System.out);
34456893Sfenner            return result;
34556893Sfenner        }
34656893Sfenner
34756893Sfenner        /** Construct a fresh scope within this scope, with new owner,
34856893Sfenner         *  with a new hash table, whose contents initially are those of
34956893Sfenner         *  the table of its outer scope.
350146773Ssam         */
35156893Sfenner        public WriteableScope dupUnshared(Symbol newOwner) {
35256893Sfenner            if (shared > 0) {
353146773Ssam                //The nested Scopes might have already added something to the table, so all items
35456893Sfenner                //that don't originate in this Scope or any of its outer Scopes need to be cleared:
35556893Sfenner                Set<Scope> acceptScopes = Collections.newSetFromMap(new IdentityHashMap<>());
356146773Ssam                ScopeImpl c = this;
35756893Sfenner                while (c != null) {
35856893Sfenner                    acceptScopes.add(c);
359146773Ssam                    c = c.next;
36056893Sfenner                }
36156893Sfenner                int n = 0;
36256893Sfenner                Entry[] oldTable = this.table;
36356893Sfenner                Entry[] newTable = new Entry[this.table.length];
36456893Sfenner                for (int i = 0; i < oldTable.length; i++) {
36556893Sfenner                    Entry e = oldTable[i];
366127668Sbms                    while (e != null && e != sentinel && !acceptScopes.contains(e.scope)) {
367146773Ssam                        e = e.shadowed;
36856893Sfenner                    }
36956893Sfenner                    if (e != null) {
370146773Ssam                        n++;
371146773Ssam                        newTable[i] = e;
372146773Ssam                    }
373146773Ssam                }
374146773Ssam                return new ScopeImpl(this, newOwner, newTable, n);
375146773Ssam            } else {
376146773Ssam                return new ScopeImpl(this, newOwner, this.table.clone(), this.nelems);
377146773Ssam            }
37856893Sfenner        }
37956893Sfenner
38056893Sfenner        /** Remove all entries of this scope from its table, if shared
38156893Sfenner         *  with next.
38256893Sfenner         */
38356893Sfenner        public WriteableScope leave() {
38456893Sfenner            Assert.check(shared == 0);
38556893Sfenner            if (table != next.table) return next;
386146773Ssam            while (elems != null) {
38756893Sfenner                int hash = getIndex(elems.sym.name);
38875115Sfenner                Entry e = table[hash];
389127668Sbms                Assert.check(e == elems, elems.sym);
39056893Sfenner                table[hash] = elems.shadowed;
39156893Sfenner                elems = elems.sibling;
39256893Sfenner            }
393146773Ssam            Assert.check(next.shared > 0);
39456893Sfenner            next.shared--;
39556893Sfenner            next.nelems = nelems;
39656893Sfenner            // System.out.println("====> leaving scope " + this.hashCode() + " owned by " + this.owner + " to " + next.hashCode());
39756893Sfenner            // new Error().printStackTrace(System.out);
39856893Sfenner            return next;
399146773Ssam        }
400146773Ssam
401146773Ssam        /** Double size of hash table.
402146773Ssam         */
403146773Ssam        private void dble() {
404146773Ssam            Assert.check(shared == 0);
405146773Ssam            Entry[] oldtable = table;
406146773Ssam            Entry[] newtable = new Entry[oldtable.length * 2];
40798524Sfenner            for (ScopeImpl s = this; s != null; s = s.next) {
40875115Sfenner                if (s.table == oldtable) {
409127668Sbms                    Assert.check(s == this || s.shared != 0);
41056893Sfenner                    s.table = newtable;
41156893Sfenner                    s.hashMask = newtable.length - 1;
41256893Sfenner                }
41356893Sfenner            }
41456893Sfenner            int n = 0;
41556893Sfenner            for (int i = oldtable.length; --i >= 0; ) {
41656893Sfenner                Entry e = oldtable[i];
417146773Ssam                if (e != null && e != sentinel) {
41856893Sfenner                    table[getIndex(e.sym.name)] = e;
41956893Sfenner                    n++;
42075115Sfenner                }
421127668Sbms            }
42256893Sfenner            // We don't need to update nelems for shared inherited scopes,
42356893Sfenner            // since that gets handled by leave().
42456893Sfenner            nelems = n;
42556893Sfenner        }
42656893Sfenner
42756893Sfenner        /** Enter symbol sym in this scope.
42856893Sfenner         */
42956893Sfenner        public void enter(Symbol sym) {
43056893Sfenner            Assert.check(shared == 0);
431146773Ssam            if (nelems * 3 >= hashMask * 2)
43256893Sfenner                dble();
43375115Sfenner            int hash = getIndex(sym.name);
434146773Ssam            Entry old = table[hash];
435146773Ssam            if (old == null) {
436146773Ssam                old = sentinel;
437146773Ssam                nelems++;
43856893Sfenner            }
43975115Sfenner            Entry e = new Entry(sym, old, elems, this);
440127668Sbms            table[hash] = e;
44175115Sfenner            elems = e;
44256893Sfenner
44356893Sfenner            //notify listeners
44456893Sfenner            listeners.symbolAdded(sym, this);
44556893Sfenner        }
44656893Sfenner
44756893Sfenner        /** Remove symbol from this scope.
448146773Ssam         */
449127668Sbms        public void remove(Symbol sym) {
45075115Sfenner            Assert.check(shared == 0);
45175115Sfenner            Entry e = lookup(sym.name, candidate -> candidate == sym);
45256893Sfenner            if (e.scope == null) return;
45356893Sfenner
45456893Sfenner            // remove e from table and shadowed list;
455127668Sbms            int i = getIndex(sym.name);
45656893Sfenner            Entry te = table[i];
45756893Sfenner            if (te == e)
45875115Sfenner                table[i] = e.shadowed;
45975115Sfenner            else while (true) {
46056893Sfenner                if (te.shadowed == e) {
461127668Sbms                    te.shadowed = e.shadowed;
46256893Sfenner                    break;
46375115Sfenner                }
46475115Sfenner                te = te.shadowed;
465127668Sbms            }
46656893Sfenner
467146773Ssam            // remove e from elems and sibling list
468146773Ssam            te = elems;
469146773Ssam            if (te == e)
470146773Ssam                elems = e.sibling;
471146773Ssam            else while (true) {
472146773Ssam                if (te.sibling == e) {
473146773Ssam                    te.sibling = e.sibling;
474127668Sbms                    break;
475146773Ssam                }
476146773Ssam                te = te.sibling;
477146773Ssam            }
478127668Sbms
479127668Sbms            removeCount++;
480127668Sbms
481127668Sbms            //notify listeners
482127668Sbms            listeners.symbolRemoved(sym, this);
483127668Sbms        }
484146773Ssam
485127668Sbms        /** Enter symbol sym in this scope if not already there.
486127668Sbms         */
487127668Sbms        public void enterIfAbsent(Symbol sym) {
488127668Sbms            Assert.check(shared == 0);
489127668Sbms            Entry e = lookup(sym.name);
490127668Sbms            while (e.scope == this && e.sym.kind != sym.kind) e = e.next();
491127668Sbms            if (e.scope != this) enter(sym);
492127668Sbms        }
493127668Sbms
494127668Sbms        /** Given a class, is there already a class with same fully
495127668Sbms         *  qualified name in this (import) scope?
496146773Ssam         */
497127668Sbms        public boolean includes(Symbol c) {
498127668Sbms            for (Scope.Entry e = lookup(c.name);
499127668Sbms                 e.scope == this;
500127668Sbms                 e = e.next()) {
501127668Sbms                if (e.sym == c) return true;
502127668Sbms            }
503127668Sbms            return false;
504127668Sbms        }
505127668Sbms
506127668Sbms        /** Return the entry associated with given name, starting in
507127668Sbms         *  this scope and proceeding outwards. If no entry was found,
50856893Sfenner         *  return the sentinel, which is characterized by having a null in
509146773Ssam         *  both its scope and sym fields, whereas both fields are non-null
510146773Ssam         *  for regular entries.
511146773Ssam         */
512146773Ssam        protected Entry lookup(Name name) {
513146773Ssam            return lookup(name, noFilter);
514146773Ssam        }
515146773Ssam
51656893Sfenner        protected Entry lookup(Name name, Filter<Symbol> sf) {
51756893Sfenner            Entry e = table[getIndex(name)];
51856893Sfenner            if (e == null || e == sentinel)
51956893Sfenner                return sentinel;
52056893Sfenner            while (e.scope != null && (e.sym.name != name || (sf != null && !sf.accepts(e.sym))))
52175115Sfenner                e = e.shadowed;
522127668Sbms            return e;
52375115Sfenner        }
52498524Sfenner
52575115Sfenner        public Symbol findFirst(Name name, Filter<Symbol> sf) {
52675115Sfenner            return lookup(name, sf).sym;
52775115Sfenner        }
52875115Sfenner
52975115Sfenner        /*void dump (java.io.PrintStream out) {
530127668Sbms            out.println(this);
531127668Sbms            for (int l=0; l < table.length; l++) {
53275115Sfenner                Entry le = table[l];
53375115Sfenner                out.print("#"+l+": ");
53475115Sfenner                if (le==sentinel) out.println("sentinel");
53575115Sfenner                else if(le == null) out.println("null");
536127668Sbms                else out.println(""+le+" s:"+le.sym);
53775115Sfenner            }
53875115Sfenner        }*/
53975115Sfenner
54075115Sfenner        /** Look for slot in the table.
54175115Sfenner         *  We use open addressing with double hashing.
542147899Ssam         */
54375115Sfenner        int getIndex (Name name) {
54475115Sfenner            int h = name.hashCode();
54575115Sfenner            int i = h & hashMask;
54675115Sfenner            // The expression below is always odd, so it is guaranteed
54775115Sfenner            // to be mutually prime with table.length, a power of 2.
54875115Sfenner            int x = hashMask - ((h + (h >> 16)) << 1);
54975115Sfenner            int d = -1; // Index of a deleted item.
55075115Sfenner            for (;;) {
55175115Sfenner                Entry e = table[i];
55275115Sfenner                if (e == null)
55375115Sfenner                    return d >= 0 ? d : i;
55475115Sfenner                if (e == sentinel) {
55575115Sfenner                    // We have to keep searching even if we see a deleted item.
55675115Sfenner                    // However, remember the index in case we fail to find the name.
55775115Sfenner                    if (d < 0)
55875115Sfenner                        d = i;
55975115Sfenner                } else if (e.sym.name == name)
56075115Sfenner                    return i;
561127668Sbms                i = (i + x) & hashMask;
56275115Sfenner            }
56375115Sfenner        }
56475115Sfenner
56575115Sfenner        public boolean anyMatch(Filter<Symbol> sf) {
56675115Sfenner            return getSymbols(sf, NON_RECURSIVE).iterator().hasNext();
56775115Sfenner        }
56875115Sfenner
569127668Sbms        public Iterable<Symbol> getSymbols(final Filter<Symbol> sf,
57075115Sfenner                                           final LookupKind lookupKind) {
57175115Sfenner            return new Iterable<Symbol>() {
572127668Sbms                public Iterator<Symbol> iterator() {
57375115Sfenner                    return new Iterator<Symbol>() {
57475115Sfenner                        private ScopeImpl currScope = ScopeImpl.this;
57575115Sfenner                        private Scope.Entry currEntry = elems;
57675115Sfenner                        private int seenRemoveCount = currScope.removeCount;
57775115Sfenner                        {
57875115Sfenner                            update();
57975115Sfenner                        }
580127668Sbms
58175115Sfenner                        public boolean hasNext() {
58275115Sfenner                            if (seenRemoveCount != currScope.removeCount &&
58375115Sfenner                                currEntry != null &&
58475115Sfenner                                !currEntry.scope.includes(currEntry.sym)) {
58575115Sfenner                                doNext(); //skip entry that is no longer in the Scope
58675115Sfenner                                seenRemoveCount = currScope.removeCount;
58775115Sfenner                            }
58875115Sfenner                            return currEntry != null;
58975115Sfenner                        }
59075115Sfenner
59175115Sfenner                        public Symbol next() {
59275115Sfenner                            if (!hasNext()) {
59375115Sfenner                                throw new NoSuchElementException();
59475115Sfenner                            }
595127668Sbms
59698524Sfenner                            return doNext();
59756893Sfenner                        }
59898524Sfenner                        private Symbol doNext() {
59998524Sfenner                            Symbol sym = (currEntry == null ? null : currEntry.sym);
60098524Sfenner                            if (currEntry != null) {
60198524Sfenner                                currEntry = currEntry.sibling;
60298524Sfenner                            }
60398524Sfenner                            update();
604127668Sbms                            return sym;
60598524Sfenner                        }
60698524Sfenner
60798524Sfenner                        public void remove() {
60898524Sfenner                            throw new UnsupportedOperationException();
60956893Sfenner                        }
61056893Sfenner
61156893Sfenner                        private void update() {
61298524Sfenner                            skipToNextMatchingEntry();
61375115Sfenner                            if (lookupKind == RECURSIVE) {
61456893Sfenner                                while (currEntry == null && currScope.next != null) {
61556893Sfenner                                    currScope = currScope.next;
61698524Sfenner                                    currEntry = currScope.elems;
61798524Sfenner                                    seenRemoveCount = currScope.removeCount;
61898524Sfenner                                    skipToNextMatchingEntry();
61998524Sfenner                                }
62098524Sfenner                            }
62198524Sfenner                        }
62298524Sfenner
62356893Sfenner                        void skipToNextMatchingEntry() {
62498524Sfenner                            while (currEntry != null && sf != null && !sf.accepts(currEntry.sym)) {
62556893Sfenner                                currEntry = currEntry.sibling;
62698524Sfenner                            }
627146773Ssam                        }
628146773Ssam                    };
629146773Ssam                }
630146773Ssam            };
631146773Ssam        }
632146773Ssam
63398524Sfenner        public Iterable<Symbol> getSymbolsByName(final Name name,
63498524Sfenner                                                 final Filter<Symbol> sf,
63598524Sfenner                                                 final LookupKind lookupKind) {
63698524Sfenner            return new Iterable<Symbol>() {
63798524Sfenner                public Iterator<Symbol> iterator() {
63898524Sfenner                     return new Iterator<Symbol>() {
63998524Sfenner                        Scope.Entry currentEntry = lookup(name, sf);
64098524Sfenner                        int seenRemoveCount = currentEntry.scope != null ?
64198524Sfenner                                currentEntry.scope.removeCount : -1;
64298524Sfenner
64398524Sfenner                        public boolean hasNext() {
64498524Sfenner                            if (currentEntry.scope != null &&
64598524Sfenner                                seenRemoveCount != currentEntry.scope.removeCount &&
64698524Sfenner                                !currentEntry.scope.includes(currentEntry.sym)) {
647146773Ssam                                doNext(); //skip entry that is no longer in the Scope
648146773Ssam                            }
649146773Ssam                            return currentEntry.scope != null &&
650146773Ssam                                    (lookupKind == RECURSIVE ||
651146773Ssam                                     currentEntry.scope == ScopeImpl.this);
652146773Ssam                        }
653146773Ssam                        public Symbol next() {
65498524Sfenner                            if (!hasNext()) {
65598524Sfenner                                throw new NoSuchElementException();
65698524Sfenner                            }
657146773Ssam                            return doNext();
65898524Sfenner                        }
65998524Sfenner                        private Symbol doNext() {
66098524Sfenner                            Scope.Entry prevEntry = currentEntry;
66198524Sfenner                            currentEntry = currentEntry.next(sf);
66298524Sfenner                            return prevEntry.sym;
663146773Ssam                        }
664146773Ssam                        public void remove() {
665146773Ssam                            throw new UnsupportedOperationException();
666146773Ssam                        }
66798524Sfenner                    };
66898524Sfenner                }
66998524Sfenner            };
670146773Ssam        }
671127668Sbms
672127668Sbms        public Scope getOrigin(Symbol s) {
673127668Sbms            for (Scope.Entry e = lookup(s.name); e.scope != null ; e = e.next()) {
674127668Sbms                if (e.sym == s) {
675146773Ssam                    return this;
676146773Ssam                }
677146773Ssam            }
678127668Sbms            return null;
67998524Sfenner        }
68098524Sfenner
68198524Sfenner        @Override
68298524Sfenner        public boolean isStaticallyImported(Symbol s) {
68398524Sfenner            return false;
68498524Sfenner        }
68598524Sfenner
68698524Sfenner        public String toString() {
68798524Sfenner            StringBuilder result = new StringBuilder();
68898524Sfenner            result.append("Scope[");
68998524Sfenner            for (ScopeImpl s = this; s != null ; s = s.next) {
69098524Sfenner                if (s != this) result.append(" | ");
69198524Sfenner                for (Entry e = s.elems; e != null; e = e.sibling) {
69298524Sfenner                    if (e != s.elems) result.append(", ");
69398524Sfenner                    result.append(e.sym);
69498524Sfenner                }
69598524Sfenner            }
69698524Sfenner            result.append("]");
69798524Sfenner            return result.toString();
69898524Sfenner        }
69998524Sfenner    }
70098524Sfenner
70198524Sfenner    /** A class for scope entries.
702127668Sbms     */
70398524Sfenner    private static class Entry {
70498524Sfenner
705146773Ssam        /** The referenced symbol.
706146773Ssam         *  sym == null   iff   this == sentinel
707146773Ssam         */
708146773Ssam        public Symbol sym;
709146773Ssam
71056893Sfenner        /** An entry with the same hash code, or sentinel.
711146773Ssam         */
712146773Ssam        private Entry shadowed;
713146773Ssam
71498524Sfenner        /** Next entry in same scope.
71598524Sfenner         */
71698524Sfenner        public Entry sibling;
71756893Sfenner
71856893Sfenner        /** The entry's scope.
71998524Sfenner         *  scope == null   iff   this == sentinel
72056893Sfenner         */
72156893Sfenner        public ScopeImpl scope;
72256893Sfenner
72356893Sfenner        public Entry(Symbol sym, Entry shadowed, Entry sibling, ScopeImpl scope) {
72456893Sfenner            this.sym = sym;
72556893Sfenner            this.shadowed = shadowed;
726127668Sbms            this.sibling = sibling;
72798524Sfenner            this.scope = scope;
72856893Sfenner        }
72998524Sfenner
73098524Sfenner        /** Return next entry with the same name as this entry, proceeding
73156893Sfenner         *  outwards if not found in this scope.
73275115Sfenner         */
73356893Sfenner        public Entry next() {
73456893Sfenner            return shadowed;
73556893Sfenner        }
73656893Sfenner
73756893Sfenner        public Entry next(Filter<Symbol> sf) {
738127668Sbms            if (shadowed.sym == null || sf == null || sf.accepts(shadowed.sym)) return shadowed;
73956893Sfenner            else return shadowed.next(sf);
74075115Sfenner        }
74156893Sfenner
74275115Sfenner    }
743146773Ssam
744146773Ssam    public static class ImportScope extends CompoundScope {
745146773Ssam
746146773Ssam        public ImportScope(Symbol owner) {
747146773Ssam            super(owner);
748146773Ssam        }
749146773Ssam
750146773Ssam        /**Finalize the content of the ImportScope to speed-up future lookups.
751146773Ssam         * No further changes to class hierarchy or class content will be reflected.
752146773Ssam         */
753146773Ssam        public void finalizeScope() {
754146773Ssam            for (List<Scope> scopes = this.subScopes; scopes.nonEmpty(); scopes = scopes.tail) {
755146773Ssam                Scope impScope = scopes.head;
756146773Ssam
757146773Ssam                if (impScope instanceof FilterImportScope && impScope.owner.kind == Kind.TYP) {
758146773Ssam                    WriteableScope finalized = WriteableScope.create(impScope.owner);
759146773Ssam
760146773Ssam                    for (Symbol sym : impScope.getSymbols()) {
761146773Ssam                        finalized.enter(sym);
762146773Ssam                    }
763146773Ssam
764146773Ssam                    finalized.listeners.add(new ScopeListener() {
765146773Ssam                        @Override
766146773Ssam                        public void symbolAdded(Symbol sym, Scope s) {
767146773Ssam                            Assert.error("The scope is sealed.");
768146773Ssam                        }
769146773Ssam
770146773Ssam                        @Override
771146773Ssam                        public void symbolRemoved(Symbol sym, Scope s) {
772146773Ssam                            Assert.error("The scope is sealed.");
773146773Ssam                        }
774146773Ssam                    });
775146773Ssam
776146773Ssam                    scopes.head = finalized;
777146773Ssam                }
778146773Ssam            }
779146773Ssam        }
780146773Ssam
781146773Ssam    }
782146773Ssam
783146773Ssam    public static class NamedImportScope extends ImportScope {
784146773Ssam
785146773Ssam        public NamedImportScope(Symbol owner, Scope currentFileScope) {
786146773Ssam            super(owner);
787146773Ssam            prependSubScope(currentFileScope);
788146773Ssam        }
789146773Ssam
790146773Ssam        public Scope importByName(Types types, Scope origin, Name name, ImportFilter filter, JCImport imp, BiConsumer<JCImport, CompletionFailure> cfHandler) {
791146773Ssam            return appendScope(new FilterImportScope(types, origin, name, filter, imp, cfHandler));
792146773Ssam        }
793146773Ssam
794146773Ssam        public Scope importType(Scope delegate, Scope origin, Symbol sym) {
795146773Ssam            return appendScope(new SingleEntryScope(delegate.owner, sym, origin));
796146773Ssam        }
797146773Ssam
798146773Ssam        private Scope appendScope(Scope newScope) {
799146773Ssam            List<Scope> existingScopes = this.subScopes.reverse();
800146773Ssam            subScopes = List.of(existingScopes.head);
801146773Ssam            subScopes = subScopes.prepend(newScope);
802146773Ssam            for (Scope s : existingScopes.tail) {
803146773Ssam                subScopes = subScopes.prepend(s);
804146773Ssam            }
805146773Ssam            return newScope;
806146773Ssam        }
807146773Ssam
808146773Ssam        private static class SingleEntryScope extends Scope {
809146773Ssam
810146773Ssam            private final Symbol sym;
811146773Ssam            private final List<Symbol> content;
812146773Ssam            private final Scope origin;
813146773Ssam
814146773Ssam            public SingleEntryScope(Symbol owner, Symbol sym, Scope origin) {
815146773Ssam                super(owner);
816146773Ssam                this.sym = sym;
817146773Ssam                this.content = List.of(sym);
818146773Ssam                this.origin = origin;
819146773Ssam            }
820146773Ssam
821146773Ssam            @Override
822146773Ssam            public Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind) {
823146773Ssam                return sf == null || sf.accepts(sym) ? content : Collections.<Symbol>emptyList();
824146773Ssam            }
825146773Ssam
826146773Ssam            @Override
827146773Ssam            public Iterable<Symbol> getSymbolsByName(Name name,
828146773Ssam                                                     Filter<Symbol> sf,
829146773Ssam                                                     LookupKind lookupKind) {
830146773Ssam                return sym.name == name &&
831146773Ssam                       (sf == null || sf.accepts(sym)) ? content : Collections.<Symbol>emptyList();
832146773Ssam            }
833146773Ssam
834146773Ssam            @Override
835146773Ssam            public Scope getOrigin(Symbol byName) {
836146773Ssam                return sym == byName ? origin : null;
837146773Ssam            }
838146773Ssam
839146773Ssam            @Override
840146773Ssam            public boolean isStaticallyImported(Symbol byName) {
841146773Ssam                return false;
842146773Ssam            }
843146773Ssam
844146773Ssam        }
845146773Ssam    }
846146773Ssam
847146773Ssam    public static class StarImportScope extends ImportScope {
848146773Ssam
849146773Ssam        public StarImportScope(Symbol owner) {
850146773Ssam            super(owner);
851146773Ssam        }
852146773Ssam
853146773Ssam        public void importAll(Types types, Scope origin,
854146773Ssam                              ImportFilter filter,
855146773Ssam                              JCImport imp,
856146773Ssam                              BiConsumer<JCImport, CompletionFailure> cfHandler) {
857146773Ssam            for (Scope existing : subScopes) {
858146773Ssam                Assert.check(existing instanceof FilterImportScope);
859146773Ssam                FilterImportScope fis = (FilterImportScope) existing;
860146773Ssam                if (fis.origin == origin && fis.filter == filter &&
861146773Ssam                    fis.imp.staticImport == imp.staticImport)
862146773Ssam                    return ; //avoid entering the same scope twice
863146773Ssam            }
864146773Ssam            prependSubScope(new FilterImportScope(types, origin, null, filter, imp, cfHandler));
86575115Sfenner        }
86675115Sfenner
86775115Sfenner        public boolean isFilled() {
86875115Sfenner            return subScopes.nonEmpty();
86975115Sfenner        }
87075115Sfenner
87175115Sfenner    }
87275115Sfenner
87375115Sfenner    public interface ImportFilter {
87475115Sfenner        public boolean accepts(Scope origin, Symbol sym);
87575115Sfenner    }
87675115Sfenner
87775115Sfenner    private static class FilterImportScope extends Scope {
87875115Sfenner
87975115Sfenner        private final Types types;
88075115Sfenner        private final Scope origin;
88175115Sfenner        private final Name  filterName;
88275115Sfenner        private final ImportFilter filter;
88375115Sfenner        private final JCImport imp;
88475115Sfenner        private final BiConsumer<JCImport, CompletionFailure> cfHandler;
88575115Sfenner
88675115Sfenner        public FilterImportScope(Types types,
88775115Sfenner                                 Scope origin,
88875115Sfenner                                 Name  filterName,
88975115Sfenner                                 ImportFilter filter,
89075115Sfenner                                 JCImport imp,
89175115Sfenner                                 BiConsumer<JCImport, CompletionFailure> cfHandler) {
89275115Sfenner            super(origin.owner);
89375115Sfenner            this.types = types;
89475115Sfenner            this.origin = origin;
89575115Sfenner            this.filterName = filterName;
89675115Sfenner            this.filter = filter;
89775115Sfenner            this.imp = imp;
89875115Sfenner            this.cfHandler = cfHandler;
89975115Sfenner        }
900127668Sbms
901127668Sbms        @Override
90275115Sfenner        public Iterable<Symbol> getSymbols(final Filter<Symbol> sf, final LookupKind lookupKind) {
90375115Sfenner            if (filterName != null)
90475115Sfenner                return getSymbolsByName(filterName, sf, lookupKind);
90575115Sfenner            try {
906127668Sbms                SymbolImporter si = new SymbolImporter(imp.staticImport) {
90775115Sfenner                    @Override
90875115Sfenner                    Iterable<Symbol> doLookup(TypeSymbol tsym) {
909127668Sbms                        return tsym.members().getSymbols(sf, lookupKind);
910127668Sbms                    }
91175115Sfenner                };
91275115Sfenner                return si.importFrom((TypeSymbol) origin.owner) :: iterator;
91375115Sfenner            } catch (CompletionFailure cf) {
91475115Sfenner                cfHandler.accept(imp, cf);
91575115Sfenner                return Collections.emptyList();
91675115Sfenner            }
91775115Sfenner        }
91875115Sfenner
919146773Ssam        @Override
92075115Sfenner        public Iterable<Symbol> getSymbolsByName(final Name name,
92175115Sfenner                                                 final Filter<Symbol> sf,
922146773Ssam                                                 final LookupKind lookupKind) {
92375115Sfenner            if (filterName != null && filterName != name)
92475115Sfenner                return Collections.emptyList();
92575115Sfenner            try {
92675115Sfenner                SymbolImporter si = new SymbolImporter(imp.staticImport) {
927127668Sbms                    @Override
92875115Sfenner                    Iterable<Symbol> doLookup(TypeSymbol tsym) {
92975115Sfenner                        return tsym.members().getSymbolsByName(name, sf, lookupKind);
93075115Sfenner                    }
93175115Sfenner                };
93275115Sfenner                return si.importFrom((TypeSymbol) origin.owner) :: iterator;
933127668Sbms            } catch (CompletionFailure cf) {
93475115Sfenner                cfHandler.accept(imp, cf);
93575115Sfenner                return Collections.emptyList();
93675115Sfenner            }
93775115Sfenner        }
93875115Sfenner
93975115Sfenner        @Override
94075115Sfenner        public Scope getOrigin(Symbol byName) {
94175115Sfenner            return origin;
94275115Sfenner        }
94375115Sfenner
94475115Sfenner        @Override
94575115Sfenner        public boolean isStaticallyImported(Symbol byName) {
94675115Sfenner            return imp.staticImport;
94775115Sfenner        }
94875115Sfenner
94975115Sfenner        abstract class SymbolImporter {
95075115Sfenner            Set<Symbol> processed = new HashSet<>();
95175115Sfenner            List<Iterable<Symbol>> delegates = List.nil();
95275115Sfenner            final boolean inspectSuperTypes;
95375115Sfenner            public SymbolImporter(boolean inspectSuperTypes) {
95475115Sfenner                this.inspectSuperTypes = inspectSuperTypes;
95575115Sfenner            }
95675115Sfenner            Stream<Symbol> importFrom(TypeSymbol tsym) {
95775115Sfenner                if (tsym == null || !processed.add(tsym))
95875115Sfenner                    return Stream.empty();
95975115Sfenner
96075115Sfenner                Stream<Symbol> result = Stream.empty();
96175115Sfenner
96275115Sfenner                if (inspectSuperTypes) {
96375115Sfenner                    // also import inherited names
96475115Sfenner                    result = importFrom(types.supertype(tsym.type).tsym);
96575115Sfenner                    for (Type t : types.interfaces(tsym.type))
96675115Sfenner                        result = Stream.concat(importFrom(t.tsym), result);
96775115Sfenner                }
96875115Sfenner
96975115Sfenner                return Stream.concat(StreamSupport.stream(doLookup(tsym).spliterator(), false)
97075115Sfenner                                                  .filter(s -> filter.accepts(origin, s)),
97175115Sfenner                                     result);
97275115Sfenner            }
97375115Sfenner            abstract Iterable<Symbol> doLookup(TypeSymbol tsym);
97475115Sfenner        }
97575115Sfenner
97675115Sfenner    }
97775115Sfenner
97875115Sfenner    /** A class scope adds capabilities to keep track of changes in related
97975115Sfenner     *  class scopes - this allows client to realize whether a class scope
98075115Sfenner     *  has changed, either directly (because a new member has been added/removed
98175115Sfenner     *  to this scope) or indirectly (i.e. because a new member has been
98275115Sfenner     *  added/removed into a supertype scope)
98375115Sfenner     */
98475115Sfenner    public static class CompoundScope extends Scope implements ScopeListener {
98575115Sfenner
98675115Sfenner        List<Scope> subScopes = List.nil();
98775115Sfenner        private int mark = 0;
98875115Sfenner
98975115Sfenner        public CompoundScope(Symbol owner) {
99075115Sfenner            super(owner);
99175115Sfenner        }
99275115Sfenner
99375115Sfenner        public void prependSubScope(Scope that) {
99475115Sfenner           if (that != null) {
99575115Sfenner                subScopes = subScopes.prepend(that);
99675115Sfenner                that.listeners.add(this);
99775115Sfenner                mark++;
99875115Sfenner                listeners.symbolAdded(null, this);
99975115Sfenner           }
100075115Sfenner        }
100175115Sfenner
100275115Sfenner        public void symbolAdded(Symbol sym, Scope s) {
100375115Sfenner            mark++;
100475115Sfenner            listeners.symbolAdded(sym, s);
100575115Sfenner        }
100675115Sfenner
100775115Sfenner        public void symbolRemoved(Symbol sym, Scope s) {
100875115Sfenner            mark++;
100975115Sfenner            listeners.symbolRemoved(sym, s);
101075115Sfenner        }
101175115Sfenner
101275115Sfenner        public int getMark() {
101375115Sfenner            return mark;
101475115Sfenner        }
101575115Sfenner
101675115Sfenner        @Override
101775115Sfenner        public String toString() {
101875115Sfenner            StringBuilder buf = new StringBuilder();
101975115Sfenner            buf.append("CompoundScope{");
102075115Sfenner            String sep = "";
102175115Sfenner            for (Scope s : subScopes) {
102275115Sfenner                buf.append(sep);
102375115Sfenner                buf.append(s);
102475115Sfenner                sep = ",";
102575115Sfenner            }
102675115Sfenner            buf.append("}");
102775115Sfenner            return buf.toString();
102875115Sfenner        }
102975115Sfenner
103075115Sfenner        @Override
103175115Sfenner        public Iterable<Symbol> getSymbols(final Filter<Symbol> sf,
103275115Sfenner                                           final LookupKind lookupKind) {
103375115Sfenner            return () -> Iterators.createCompoundIterator(subScopes,
103475115Sfenner                                                          scope -> scope.getSymbols(sf,
103575115Sfenner                                                                                    lookupKind)
103675115Sfenner                                                                        .iterator());
103775115Sfenner        }
1038146773Ssam
103975115Sfenner        @Override
104075115Sfenner        public Iterable<Symbol> getSymbolsByName(final Name name,
104175115Sfenner                                                 final Filter<Symbol> sf,
104275115Sfenner                                                 final LookupKind lookupKind) {
104375115Sfenner            return () -> Iterators.createCompoundIterator(subScopes,
104475115Sfenner                                                          scope -> scope.getSymbolsByName(name,
104575115Sfenner                                                                                          sf,
104675115Sfenner                                                                                          lookupKind)
104775115Sfenner                                                                        .iterator());
104875115Sfenner        }
104975115Sfenner
105075115Sfenner        @Override
105175115Sfenner        public Scope getOrigin(Symbol sym) {
105275115Sfenner            for (Scope delegate : subScopes) {
105375115Sfenner                if (delegate.includes(sym))
105475115Sfenner                    return delegate.getOrigin(sym);
105575115Sfenner            }
105675115Sfenner
105775115Sfenner            return null;
105875115Sfenner        }
105975115Sfenner
106075115Sfenner        @Override
106175115Sfenner        public boolean isStaticallyImported(Symbol sym) {
106275115Sfenner            for (Scope delegate : subScopes) {
106375115Sfenner                if (delegate.includes(sym))
106475115Sfenner                    return delegate.isStaticallyImported(sym);
106575115Sfenner            }
106675115Sfenner
106775115Sfenner            return false;
106875115Sfenner        }
1069127668Sbms
107075115Sfenner    }
107175115Sfenner
107275115Sfenner    /** An error scope, for which the owner should be an error symbol. */
107375115Sfenner    public static class ErrorScope extends ScopeImpl {
107475115Sfenner        ErrorScope(ScopeImpl next, Symbol errSymbol, Entry[] table) {
107575115Sfenner            super(next, /*owner=*/errSymbol, table);
107675115Sfenner        }
107775115Sfenner        public ErrorScope(Symbol errSymbol) {
107875115Sfenner            super(errSymbol);
107975115Sfenner        }
108075115Sfenner        public WriteableScope dup(Symbol newOwner) {
108175115Sfenner            return new ErrorScope(this, newOwner, table);
1082127668Sbms        }
108375115Sfenner        public WriteableScope dupUnshared(Symbol newOwner) {
108475115Sfenner            return new ErrorScope(this, newOwner, table.clone());
108575115Sfenner        }
108675115Sfenner        public Entry lookup(Name name) {
108775115Sfenner            Entry e = super.lookup(name);
108875115Sfenner            if (e.scope == null)
108975115Sfenner                return new Entry(owner, null, null, null);
109075115Sfenner            else
109175115Sfenner                return e;
109275115Sfenner        }
109375115Sfenner    }
109475115Sfenner}
109575115Sfenner