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