src/share/classes/com/sun/tools/javac/code/Scope.java

Fri, 12 Nov 2010 12:34:18 +0000

author
mcimadamore
date
Fri, 12 Nov 2010 12:34:18 +0000
changeset 743
6a99b741a1b0
parent 738
9427a3c795fc
child 751
abaceae7c9f8
permissions
-rw-r--r--

6970016: Clean up ARM/try-with-resources implementation
Summary: changed Xlint option name from -Xlint:arm to -Xlint:try
Reviewed-by: jjg

     1 /*
     2  * Copyright (c) 1999, 2010, 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  */
    26 package com.sun.tools.javac.code;
    28 import com.sun.tools.javac.util.*;
    29 import java.util.Iterator;
    31 /** A scope represents an area of visibility in a Java program. The
    32  *  Scope class is a container for symbols which provides
    33  *  efficient access to symbols given their names. Scopes are implemented
    34  *  as hash tables with "open addressing" and "double hashing".
    35  *  Scopes can be nested; the next field of a scope points
    36  *  to its next outer scope. Nested scopes can share their hash tables.
    37  *
    38  *  <p><b>This is NOT part of any supported API.
    39  *  If you write code that depends on this, you do so at your own risk.
    40  *  This code and its internal interfaces are subject to change or
    41  *  deletion without notice.</b>
    42  */
    43 public class Scope {
    45     /** The number of scopes that share this scope's hash table.
    46      */
    47     private int shared;
    49     /** Next enclosing scope (with whom this scope may share a hashtable)
    50      */
    51     public Scope next;
    53     /** The scope's owner.
    54      */
    55     public Symbol owner;
    57     /** A hash table for the scope's entries.
    58      */
    59     Entry[] table;
    61     /** Mask for hash codes, always equal to (table.length - 1).
    62      */
    63     int hashMask;
    65     /** A linear list that also contains all entries in
    66      *  reverse order of appearance (i.e later entries are pushed on top).
    67      */
    68     public Entry elems;
    70     /** The number of elements in this scope.
    71      * This includes deleted elements, whose value is the sentinel.
    72      */
    73     int nelems = 0;
    75     /** A timestamp - useful to quickly check whether a scope has changed or not
    76      */
    77     public ScopeCounter scopeCounter;
    79     static ScopeCounter dummyCounter = new ScopeCounter() {
    80         @Override
    81         public void inc() {
    82             //do nothing
    83         }
    84     };
    86     public static class ScopeCounter {
    87         protected static final Context.Key<ScopeCounter> scopeCounterKey =
    88             new Context.Key<ScopeCounter>();
    90         public static ScopeCounter instance(Context context) {
    91             ScopeCounter instance = context.get(scopeCounterKey);
    92             if (instance == null)
    93                 instance = new ScopeCounter(context);
    94             return instance;
    95         }
    97         protected ScopeCounter(Context context) {
    98             context.put(scopeCounterKey, this);
    99         }
   101         private ScopeCounter() {};
   103         private long val = 0;
   105         public void inc() {
   106             val++;
   107         }
   109         public long val() {
   110             return val;
   111         }
   112     }
   114     /** Use as a "not-found" result for lookup.
   115      * Also used to mark deleted entries in the table.
   116      */
   117     private static final Entry sentinel = new Entry(null, null, null, null);
   119     /** The hash table's initial size.
   120      */
   121     private static final int INITIAL_SIZE = 0x10;
   123     /** A value for the empty scope.
   124      */
   125     public static final Scope emptyScope = new Scope(null, null, new Entry[]{}, dummyCounter);
   127     /** Construct a new scope, within scope next, with given owner, using
   128      *  given table. The table's length must be an exponent of 2.
   129      */
   130     private Scope(Scope next, Symbol owner, Entry[] table, ScopeCounter scopeCounter) {
   131         this.next = next;
   132         assert emptyScope == null || owner != null;
   133         this.owner = owner;
   134         this.table = table;
   135         this.hashMask = table.length - 1;
   136         this.scopeCounter = scopeCounter;
   137     }
   139     /** Convenience constructor used for dup and dupUnshared. */
   140     private Scope(Scope next, Symbol owner, Entry[] table) {
   141         this(next, owner, table, next.scopeCounter);
   142         this.nelems = next.nelems;
   143     }
   145     /** Construct a new scope, within scope next, with given owner,
   146      *  using a fresh table of length INITIAL_SIZE.
   147      */
   148     public Scope(Symbol owner) {
   149         this(owner, dummyCounter);
   150     }
   152     protected Scope(Symbol owner, ScopeCounter scopeCounter) {
   153         this(null, owner, new Entry[INITIAL_SIZE], scopeCounter);
   154     }
   156     /** Construct a fresh scope within this scope, with same owner,
   157      *  which shares its table with the outer scope. Used in connection with
   158      *  method leave if scope access is stack-like in order to avoid allocation
   159      *  of fresh tables.
   160      */
   161     public Scope dup() {
   162         return dup(this.owner);
   163     }
   165     /** Construct a fresh scope within this scope, with new owner,
   166      *  which shares its table with the outer scope. Used in connection with
   167      *  method leave if scope access is stack-like in order to avoid allocation
   168      *  of fresh tables.
   169      */
   170     public Scope dup(Symbol newOwner) {
   171         Scope result = new Scope(this, newOwner, this.table);
   172         shared++;
   173         // System.out.println("====> duping scope " + this.hashCode() + " owned by " + newOwner + " to " + result.hashCode());
   174         // new Error().printStackTrace(System.out);
   175         return result;
   176     }
   178     /** Construct a fresh scope within this scope, with same owner,
   179      *  with a new hash table, whose contents initially are those of
   180      *  the table of its outer scope.
   181      */
   182     public Scope dupUnshared() {
   183         return new Scope(this, this.owner, this.table.clone());
   184     }
   186     /** Remove all entries of this scope from its table, if shared
   187      *  with next.
   188      */
   189     public Scope leave() {
   190         assert shared == 0;
   191         if (table != next.table) return next;
   192         while (elems != null) {
   193             int hash = getIndex(elems.sym.name);
   194             Entry e = table[hash];
   195             assert e == elems : elems.sym;
   196             table[hash] = elems.shadowed;
   197             elems = elems.sibling;
   198         }
   199         assert next.shared > 0;
   200         next.shared--;
   201         next.nelems = nelems;
   202         // System.out.println("====> leaving scope " + this.hashCode() + " owned by " + this.owner + " to " + next.hashCode());
   203         // new Error().printStackTrace(System.out);
   204         return next;
   205     }
   207     /** Double size of hash table.
   208      */
   209     private void dble() {
   210         assert shared == 0;
   211         Entry[] oldtable = table;
   212         Entry[] newtable = new Entry[oldtable.length * 2];
   213         for (Scope s = this; s != null; s = s.next) {
   214             if (s.table == oldtable) {
   215                 assert s == this || s.shared != 0;
   216                 s.table = newtable;
   217                 s.hashMask = newtable.length - 1;
   218             }
   219         }
   220         int n = 0;
   221         for (int i = oldtable.length; --i >= 0; ) {
   222             Entry e = oldtable[i];
   223             if (e != null && e != sentinel && ! e.isBogus()) {
   224                 table[getIndex(e.sym.name)] = e;
   225                 n++;
   226             }
   227         }
   228         // We don't need to update nelems for shared inherited scopes,
   229         // since that gets handled by leave().
   230         nelems = n;
   231     }
   233     /** Enter symbol sym in this scope.
   234      */
   235     public void enter(Symbol sym) {
   236         assert shared == 0;
   237         enter(sym, this);
   238     }
   240     public void enter(Symbol sym, Scope s) {
   241         enter(sym, s, s);
   242     }
   244     /**
   245      * Enter symbol sym in this scope, but mark that it comes from
   246      * given scope `s' accessed through `origin'.  The last two
   247      * arguments are only used in import scopes.
   248      */
   249     public void enter(Symbol sym, Scope s, Scope origin) {
   250         assert shared == 0;
   251         if (nelems * 3 >= hashMask * 2)
   252             dble();
   253         int hash = getIndex(sym.name);
   254         Entry old = table[hash];
   255         if (old == null) {
   256             old = sentinel;
   257             nelems++;
   258         }
   259         Entry e = makeEntry(sym, old, elems, s, origin);
   260         table[hash] = e;
   261         elems = e;
   262         scopeCounter.inc();
   263     }
   265     Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
   266         return new Entry(sym, shadowed, sibling, scope);
   267     }
   269     /** Remove symbol from this scope.  Used when an inner class
   270      *  attribute tells us that the class isn't a package member.
   271      */
   272     public void remove(Symbol sym) {
   273         assert shared == 0;
   274         Entry e = lookup(sym.name);
   275         if (e.scope == null) return;
   277         scopeCounter.inc();
   279         // remove e from table and shadowed list;
   280         int i = getIndex(sym.name);
   281         Entry te = table[i];
   282         if (te == e)
   283             table[i] = e.shadowed;
   284         else while (true) {
   285             if (te.shadowed == e) {
   286                 te.shadowed = e.shadowed;
   287                 break;
   288             }
   289             te = te.shadowed;
   290         }
   292         // remove e from elems and sibling list
   293         te = elems;
   294         if (te == e)
   295             elems = e.sibling;
   296         else while (true) {
   297             if (te.sibling == e) {
   298                 te.sibling = e.sibling;
   299                 break;
   300             }
   301             te = te.sibling;
   302         }
   303     }
   305     /** Enter symbol sym in this scope if not already there.
   306      */
   307     public void enterIfAbsent(Symbol sym) {
   308         assert shared == 0;
   309         Entry e = lookup(sym.name);
   310         while (e.scope == this && e.sym.kind != sym.kind) e = e.next();
   311         if (e.scope != this) enter(sym);
   312     }
   314     /** Given a class, is there already a class with same fully
   315      *  qualified name in this (import) scope?
   316      */
   317     public boolean includes(Symbol c) {
   318         for (Scope.Entry e = lookup(c.name);
   319              e.scope == this;
   320              e = e.next()) {
   321             if (e.sym == c) return true;
   322         }
   323         return false;
   324     }
   326     static final Filter<Symbol> noFilter = new Filter<Symbol>() {
   327         public boolean accepts(Symbol s) {
   328             return true;
   329         }
   330     };
   332     /** Return the entry associated with given name, starting in
   333      *  this scope and proceeding outwards. If no entry was found,
   334      *  return the sentinel, which is characterized by having a null in
   335      *  both its scope and sym fields, whereas both fields are non-null
   336      *  for regular entries.
   337      */
   338     public Entry lookup(Name name) {
   339         return lookup(name, noFilter);
   340     }
   341     public Entry lookup(Name name, Filter<Symbol> sf) {
   342         Entry e = table[getIndex(name)];
   343         if (e == null || e == sentinel)
   344             return sentinel;
   345         while (e.scope != null && (e.sym.name != name || !sf.accepts(e.sym)))
   346             e = e.shadowed;
   347         return e;
   348     }
   350     /*void dump (java.io.PrintStream out) {
   351         out.println(this);
   352         for (int l=0; l < table.length; l++) {
   353             Entry le = table[l];
   354             out.print("#"+l+": ");
   355             if (le==sentinel) out.println("sentinel");
   356             else if(le == null) out.println("null");
   357             else out.println(""+le+" s:"+le.sym);
   358         }
   359     }*/
   361     /** Look for slot in the table.
   362      *  We use open addressing with double hashing.
   363      */
   364     int getIndex (Name name) {
   365         int h = name.hashCode();
   366         int i = h & hashMask;
   367         // The expression below is always odd, so it is guaranteed
   368         // be be mutually prime with table.length, a power of 2.
   369         int x = hashMask - ((h + (h >> 16)) << 1);
   370         int d = -1; // Index of a deleted item.
   371         for (;;) {
   372             Entry e = table[i];
   373             if (e == null)
   374                 return d >= 0 ? d : i;
   375             if (e == sentinel) {
   376                 // We have to keep searching even if we see a deleted item.
   377                 // However, remember the index in case we fail to find the name.
   378                 if (d < 0)
   379                     d = i;
   380             } else if (e.sym.name == name)
   381                 return i;
   382             i = (i + x) & hashMask;
   383         }
   384     }
   386     public Iterable<Symbol> getElements() {
   387         return getElements(noFilter);
   388     }
   390     public Iterable<Symbol> getElements(final Filter<Symbol> sf) {
   391         return new Iterable<Symbol>() {
   392             public Iterator<Symbol> iterator() {
   393                 return new Iterator<Symbol>() {
   394                     private Scope currScope = Scope.this;
   395                     private Scope.Entry currEntry = elems;
   396                     {
   397                         update();
   398                     }
   400                     public boolean hasNext() {
   401                         return currEntry != null;
   402                     }
   404                     public Symbol next() {
   405                         Symbol sym = (currEntry == null ? null : currEntry.sym);
   406                         if (currEntry != null) {
   407                             currEntry = currEntry.sibling;
   408                         }
   409                         update();
   410                         return sym;
   411                     }
   413                     public void remove() {
   414                         throw new UnsupportedOperationException();
   415                     }
   417                     private void update() {
   418                         skipToNextMatchingEntry();
   419                         while (currEntry == null && currScope.next != null) {
   420                             currScope = currScope.next;
   421                             currEntry = currScope.elems;
   422                             skipToNextMatchingEntry();
   423                         }
   424                     }
   426                     void skipToNextMatchingEntry() {
   427                         while (currEntry != null && !sf.accepts(currEntry.sym)) {
   428                             currEntry = currEntry.sibling;
   429                         }
   430                     }
   431                 };
   432             }
   433         };
   435     }
   437     public String toString() {
   438         StringBuilder result = new StringBuilder();
   439         result.append("Scope[");
   440         for (Scope s = this; s != null ; s = s.next) {
   441             if (s != this) result.append(" | ");
   442             for (Entry e = s.elems; e != null; e = e.sibling) {
   443                 if (e != s.elems) result.append(", ");
   444                 result.append(e.sym);
   445             }
   446         }
   447         result.append("]");
   448         return result.toString();
   449     }
   451     /** A class for scope entries.
   452      */
   453     public static class Entry {
   455         /** The referenced symbol.
   456          *  sym == null   iff   this == sentinel
   457          */
   458         public Symbol sym;
   460         /** An entry with the same hash code, or sentinel.
   461          */
   462         private Entry shadowed;
   464         /** Next entry in same scope.
   465          */
   466         public Entry sibling;
   468         /** The entry's scope.
   469          *  scope == null   iff   this == sentinel
   470          *  for an entry in an import scope, this is the scope
   471          *  where the entry came from (i.e. was imported from).
   472          */
   473         public Scope scope;
   475         public Entry(Symbol sym, Entry shadowed, Entry sibling, Scope scope) {
   476             this.sym = sym;
   477             this.shadowed = shadowed;
   478             this.sibling = sibling;
   479             this.scope = scope;
   480         }
   482         /** Return next entry with the same name as this entry, proceeding
   483          *  outwards if not found in this scope.
   484          */
   485         public Entry next() {
   486             return shadowed;
   487         }
   489         public Scope getOrigin() {
   490             // The origin is only recorded for import scopes.  For all
   491             // other scope entries, the "enclosing" type is available
   492             // from other sources.  See Attr.visitSelect and
   493             // Attr.visitIdent.  Rather than throwing an assertion
   494             // error, we return scope which will be the same as origin
   495             // in many cases.
   496             return scope;
   497         }
   499         protected boolean isBogus () { return false; }
   500     }
   502     public static class ImportScope extends Scope {
   504         public ImportScope(Symbol owner) {
   505             super(owner);
   506         }
   508         @Override
   509         Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
   510             return new ImportEntry(sym, shadowed, sibling, scope, origin);
   511         }
   513         public Entry lookup(Name name) {
   514             Entry e = table[getIndex(name)];
   515             if (e == null)
   516                 return sentinel;
   517             while (e.isBogus())
   518                 e = e.shadowed;
   519             return e;
   520         }
   522         static class ImportEntry extends Entry {
   523             private Scope origin;
   525             ImportEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
   526                 super(sym, shadowed, sibling, scope);
   527                 this.origin = origin;
   528             }
   529             public Entry next() {
   530                 Entry e = super.shadowed;
   531                 while (isBogus())
   532                     e = e.shadowed;
   533                 return e;
   534             }
   536             @Override
   537             public Scope getOrigin() { return origin; }
   539             /**
   540              * Is this a bogus inner-class import?
   541              * An inner class {@code Outer$Inner.class} read from a class file
   542              * starts out in a package scope under the name {@code Outer$Inner},
   543              * which (if star-imported) gets copied to the import scope.
   544              * When the InnerClasses attribute is processed, the ClassSymbol
   545              * is renamed in place (to {@code Inner}), and the owner changed
   546              * to the {@code Outer} class.  The ImportScope still has the old
   547              * Entry that was created and hashed as {@code "Outer$Inner"},
   548              * but whose name was changed to {@code "Inner"}.  This violates
   549              * the invariants for the Scope hash table, and so is pretty bogus.
   550              * When the symbol was renamed, it should have been removed from
   551              * the import scope (and not just the package scope); however,
   552              * doing so is difficult.  A better fix would be to change
   553              * import scopes to indirectly reference package symbols, rather
   554              * than copy from them.
   555              * Until then, we detect and skip the bogus entries using this test.
   556              */
   557             protected boolean isBogus () { return sym.owner != scope.owner; }
   558         }
   559     }
   561     /** An empty scope, into which you can't place anything.  Used for
   562      *  the scope for a variable initializer.
   563      */
   564     public static class DelegatedScope extends Scope {
   565         Scope delegatee;
   566         public static final Entry[] emptyTable = new Entry[0];
   568         public DelegatedScope(Scope outer) {
   569             super(outer, outer.owner, emptyTable, outer.scopeCounter);
   570             delegatee = outer;
   571         }
   572         public Scope dup() {
   573             return new DelegatedScope(next);
   574         }
   575         public Scope dupUnshared() {
   576             return new DelegatedScope(next);
   577         }
   578         public Scope leave() {
   579             return next;
   580         }
   581         public void enter(Symbol sym) {
   582             // only anonymous classes could be put here
   583         }
   584         public void enter(Symbol sym, Scope s) {
   585             // only anonymous classes could be put here
   586         }
   587         public void remove(Symbol sym) {
   588             throw new AssertionError(sym);
   589         }
   590         public Entry lookup(Name name) {
   591             return delegatee.lookup(name);
   592         }
   593     }
   595     /** A class scope, for which a scope counter should be provided */
   596     public static class ClassScope extends Scope {
   598         ClassScope(Scope next, Symbol owner, Entry[] table, ScopeCounter scopeCounter) {
   599             super(next, owner, table, scopeCounter);
   600         }
   602         public ClassScope(Symbol owner, ScopeCounter scopeCounter) {
   603             super(owner, scopeCounter);
   604         }
   605     }
   607     /** An error scope, for which the owner should be an error symbol. */
   608     public static class ErrorScope extends Scope {
   609         ErrorScope(Scope next, Symbol errSymbol, Entry[] table) {
   610             super(next, /*owner=*/errSymbol, table, dummyCounter);
   611         }
   612         public ErrorScope(Symbol errSymbol) {
   613             super(errSymbol);
   614         }
   615         public Scope dup() {
   616             return new ErrorScope(this, owner, table);
   617         }
   618         public Scope dupUnshared() {
   619             return new ErrorScope(this, owner, table.clone());
   620         }
   621         public Entry lookup(Name name) {
   622             Entry e = super.lookup(name);
   623             if (e.scope == null)
   624                 return new Entry(owner, null, null, null);
   625             else
   626                 return e;
   627         }
   628     }
   629 }

mercurial