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