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

changeset 738
9427a3c795fc
parent 729
6ce6ee1b831a
child 751
abaceae7c9f8
equal deleted inserted replaced
737:e406f0645b7e 738:9427a3c795fc
1 /* 1 /*
2 * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved. 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. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 * 4 *
5 * This code is free software; you can redistribute it and/or modify it 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 6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this 7 * published by the Free Software Foundation. Oracle designates this
29 import java.util.Iterator; 29 import java.util.Iterator;
30 30
31 /** A scope represents an area of visibility in a Java program. The 31 /** A scope represents an area of visibility in a Java program. The
32 * Scope class is a container for symbols which provides 32 * Scope class is a container for symbols which provides
33 * efficient access to symbols given their names. Scopes are implemented 33 * efficient access to symbols given their names. Scopes are implemented
34 * as hash tables. Scopes can be nested; the next field of a scope points 34 * as hash tables with "open addressing" and "double hashing".
35 * Scopes can be nested; the next field of a scope points
35 * to its next outer scope. Nested scopes can share their hash tables. 36 * to its next outer scope. Nested scopes can share their hash tables.
36 * 37 *
37 * <p><b>This is NOT part of any supported API. 38 * <p><b>This is NOT part of any supported API.
38 * If you write code that depends on this, you do so at your own risk. 39 * If you write code that depends on this, you do so at your own risk.
39 * This code and its internal interfaces are subject to change or 40 * This code and its internal interfaces are subject to change or
53 */ 54 */
54 public Symbol owner; 55 public Symbol owner;
55 56
56 /** A hash table for the scope's entries. 57 /** A hash table for the scope's entries.
57 */ 58 */
58 public Entry[] table; 59 Entry[] table;
59 60
60 /** Mask for hash codes, always equal to (table.length - 1). 61 /** Mask for hash codes, always equal to (table.length - 1).
61 */ 62 */
62 int hashMask; 63 int hashMask;
63 64
65 * reverse order of appearance (i.e later entries are pushed on top). 66 * reverse order of appearance (i.e later entries are pushed on top).
66 */ 67 */
67 public Entry elems; 68 public Entry elems;
68 69
69 /** The number of elements in this scope. 70 /** The number of elements in this scope.
70 */ 71 * This includes deleted elements, whose value is the sentinel.
71 public int nelems = 0; 72 */
73 int nelems = 0;
72 74
73 /** A timestamp - useful to quickly check whether a scope has changed or not 75 /** A timestamp - useful to quickly check whether a scope has changed or not
74 */ 76 */
75 public ScopeCounter scopeCounter; 77 public ScopeCounter scopeCounter;
76 78
107 public long val() { 109 public long val() {
108 return val; 110 return val;
109 } 111 }
110 } 112 }
111 113
112 /** Every hash bucket is a list of Entry's which ends in sentinel. 114 /** Use as a "not-found" result for lookup.
115 * Also used to mark deleted entries in the table.
113 */ 116 */
114 private static final Entry sentinel = new Entry(null, null, null, null); 117 private static final Entry sentinel = new Entry(null, null, null, null);
115 118
116 /** The hash table's initial size. 119 /** The hash table's initial size.
117 */ 120 */
128 this.next = next; 131 this.next = next;
129 assert emptyScope == null || owner != null; 132 assert emptyScope == null || owner != null;
130 this.owner = owner; 133 this.owner = owner;
131 this.table = table; 134 this.table = table;
132 this.hashMask = table.length - 1; 135 this.hashMask = table.length - 1;
133 this.elems = null;
134 this.nelems = 0;
135 this.shared = 0;
136 this.scopeCounter = scopeCounter; 136 this.scopeCounter = scopeCounter;
137 }
138
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;
137 } 143 }
138 144
139 /** Construct a new scope, within scope next, with given owner, 145 /** Construct a new scope, within scope next, with given owner,
140 * using a fresh table of length INITIAL_SIZE. 146 * using a fresh table of length INITIAL_SIZE.
141 */ 147 */
143 this(owner, dummyCounter); 149 this(owner, dummyCounter);
144 } 150 }
145 151
146 protected Scope(Symbol owner, ScopeCounter scopeCounter) { 152 protected Scope(Symbol owner, ScopeCounter scopeCounter) {
147 this(null, owner, new Entry[INITIAL_SIZE], scopeCounter); 153 this(null, owner, new Entry[INITIAL_SIZE], scopeCounter);
148 for (int i = 0; i < INITIAL_SIZE; i++) table[i] = sentinel;
149 } 154 }
150 155
151 /** Construct a fresh scope within this scope, with same owner, 156 /** Construct a fresh scope within this scope, with same owner,
152 * which shares its table with the outer scope. Used in connection with 157 * which shares its table with the outer scope. Used in connection with
153 * method leave if scope access is stack-like in order to avoid allocation 158 * method leave if scope access is stack-like in order to avoid allocation
154 * of fresh tables. 159 * of fresh tables.
155 */ 160 */
156 public Scope dup() { 161 public Scope dup() {
157 Scope result = new Scope(this, this.owner, this.table, scopeCounter); 162 return dup(this.owner);
158 shared++;
159 // System.out.println("====> duping scope " + this.hashCode() + " owned by " + this.owner + " to " + result.hashCode());
160 // new Error().printStackTrace(System.out);
161 return result;
162 } 163 }
163 164
164 /** Construct a fresh scope within this scope, with new owner, 165 /** Construct a fresh scope within this scope, with new owner,
165 * which shares its table with the outer scope. Used in connection with 166 * which shares its table with the outer scope. Used in connection with
166 * method leave if scope access is stack-like in order to avoid allocation 167 * method leave if scope access is stack-like in order to avoid allocation
167 * of fresh tables. 168 * of fresh tables.
168 */ 169 */
169 public Scope dup(Symbol newOwner) { 170 public Scope dup(Symbol newOwner) {
170 Scope result = new Scope(this, newOwner, this.table, scopeCounter); 171 Scope result = new Scope(this, newOwner, this.table);
171 shared++; 172 shared++;
172 // System.out.println("====> duping scope " + this.hashCode() + " owned by " + newOwner + " to " + result.hashCode()); 173 // System.out.println("====> duping scope " + this.hashCode() + " owned by " + newOwner + " to " + result.hashCode());
173 // new Error().printStackTrace(System.out); 174 // new Error().printStackTrace(System.out);
174 return result; 175 return result;
175 } 176 }
177 /** Construct a fresh scope within this scope, with same owner, 178 /** Construct a fresh scope within this scope, with same owner,
178 * with a new hash table, whose contents initially are those of 179 * with a new hash table, whose contents initially are those of
179 * the table of its outer scope. 180 * the table of its outer scope.
180 */ 181 */
181 public Scope dupUnshared() { 182 public Scope dupUnshared() {
182 return new Scope(this, this.owner, this.table.clone(), scopeCounter); 183 return new Scope(this, this.owner, this.table.clone());
183 } 184 }
184 185
185 /** Remove all entries of this scope from its table, if shared 186 /** Remove all entries of this scope from its table, if shared
186 * with next. 187 * with next.
187 */ 188 */
188 public Scope leave() { 189 public Scope leave() {
189 assert shared == 0; 190 assert shared == 0;
190 if (table != next.table) return next; 191 if (table != next.table) return next;
191 while (elems != null) { 192 while (elems != null) {
192 int hash = elems.sym.name.hashCode() & hashMask; 193 int hash = getIndex(elems.sym.name);
193 Entry e = table[hash]; 194 Entry e = table[hash];
194 assert e == elems : elems.sym; 195 assert e == elems : elems.sym;
195 table[hash] = elems.shadowed; 196 table[hash] = elems.shadowed;
196 elems = elems.sibling; 197 elems = elems.sibling;
197 } 198 }
198 assert next.shared > 0; 199 assert next.shared > 0;
199 next.shared--; 200 next.shared--;
201 next.nelems = nelems;
200 // System.out.println("====> leaving scope " + this.hashCode() + " owned by " + this.owner + " to " + next.hashCode()); 202 // System.out.println("====> leaving scope " + this.hashCode() + " owned by " + this.owner + " to " + next.hashCode());
201 // new Error().printStackTrace(System.out); 203 // new Error().printStackTrace(System.out);
202 return next; 204 return next;
203 } 205 }
204 206
213 assert s == this || s.shared != 0; 215 assert s == this || s.shared != 0;
214 s.table = newtable; 216 s.table = newtable;
215 s.hashMask = newtable.length - 1; 217 s.hashMask = newtable.length - 1;
216 } 218 }
217 } 219 }
218 for (int i = 0; i < newtable.length; i++) newtable[i] = sentinel; 220 int n = 0;
219 for (int i = 0; i < oldtable.length; i++) copy(oldtable[i]); 221 for (int i = oldtable.length; --i >= 0; ) {
220 } 222 Entry e = oldtable[i];
221 223 if (e != null && e != sentinel && ! e.isBogus()) {
222 /** Copy the given entry and all entries shadowed by it to table 224 table[getIndex(e.sym.name)] = e;
223 */ 225 n++;
224 private void copy(Entry e) { 226 }
225 if (e.sym != null) { 227 }
226 copy(e.shadowed); 228 // We don't need to update nelems for shared inherited scopes,
227 int hash = e.sym.name.hashCode() & hashMask; 229 // since that gets handled by leave().
228 e.shadowed = table[hash]; 230 nelems = n;
229 table[hash] = e;
230 }
231 } 231 }
232 232
233 /** Enter symbol sym in this scope. 233 /** Enter symbol sym in this scope.
234 */ 234 */
235 public void enter(Symbol sym) { 235 public void enter(Symbol sym) {
246 * given scope `s' accessed through `origin'. The last two 246 * given scope `s' accessed through `origin'. The last two
247 * arguments are only used in import scopes. 247 * arguments are only used in import scopes.
248 */ 248 */
249 public void enter(Symbol sym, Scope s, Scope origin) { 249 public void enter(Symbol sym, Scope s, Scope origin) {
250 assert shared == 0; 250 assert shared == 0;
251 // Temporarily disabled (bug 6460352): 251 if (nelems * 3 >= hashMask * 2)
252 // if (nelems * 3 >= hashMask * 2) dble(); 252 dble();
253 int hash = sym.name.hashCode() & hashMask; 253 int hash = getIndex(sym.name);
254 Entry e = makeEntry(sym, table[hash], elems, s, origin); 254 Entry old = table[hash];
255 if (old == null) {
256 old = sentinel;
257 nelems++;
258 }
259 Entry e = makeEntry(sym, old, elems, s, origin);
255 table[hash] = e; 260 table[hash] = e;
256 elems = e; 261 elems = e;
257 nelems++;
258 scopeCounter.inc(); 262 scopeCounter.inc();
259 } 263 }
260 264
261 Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) { 265 Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
262 return new Entry(sym, shadowed, sibling, scope); 266 return new Entry(sym, shadowed, sibling, scope);
266 * attribute tells us that the class isn't a package member. 270 * attribute tells us that the class isn't a package member.
267 */ 271 */
268 public void remove(Symbol sym) { 272 public void remove(Symbol sym) {
269 assert shared == 0; 273 assert shared == 0;
270 Entry e = lookup(sym.name); 274 Entry e = lookup(sym.name);
271 while (e.scope == this && e.sym != sym) e = e.next();
272 if (e.scope == null) return; 275 if (e.scope == null) return;
273 276
274 scopeCounter.inc(); 277 scopeCounter.inc();
275 278
276 // remove e from table and shadowed list; 279 // remove e from table and shadowed list;
277 Entry te = table[sym.name.hashCode() & hashMask]; 280 int i = getIndex(sym.name);
281 Entry te = table[i];
278 if (te == e) 282 if (te == e)
279 table[sym.name.hashCode() & hashMask] = e.shadowed; 283 table[i] = e.shadowed;
280 else while (true) { 284 else while (true) {
281 if (te.shadowed == e) { 285 if (te.shadowed == e) {
282 te.shadowed = e.shadowed; 286 te.shadowed = e.shadowed;
283 break; 287 break;
284 } 288 }
333 */ 337 */
334 public Entry lookup(Name name) { 338 public Entry lookup(Name name) {
335 return lookup(name, noFilter); 339 return lookup(name, noFilter);
336 } 340 }
337 public Entry lookup(Name name, Filter<Symbol> sf) { 341 public Entry lookup(Name name, Filter<Symbol> sf) {
338 Entry e = table[name.hashCode() & hashMask]; 342 Entry e = table[getIndex(name)];
343 if (e == null || e == sentinel)
344 return sentinel;
339 while (e.scope != null && (e.sym.name != name || !sf.accepts(e.sym))) 345 while (e.scope != null && (e.sym.name != name || !sf.accepts(e.sym)))
340 e = e.shadowed; 346 e = e.shadowed;
341 return e; 347 return e;
348 }
349
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 }*/
360
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 }
342 } 384 }
343 385
344 public Iterable<Symbol> getElements() { 386 public Iterable<Symbol> getElements() {
345 return getElements(noFilter); 387 return getElements(noFilter);
346 } 388 }
439 481
440 /** Return next entry with the same name as this entry, proceeding 482 /** Return next entry with the same name as this entry, proceeding
441 * outwards if not found in this scope. 483 * outwards if not found in this scope.
442 */ 484 */
443 public Entry next() { 485 public Entry next() {
444 Entry e = shadowed; 486 return shadowed;
445 while (e.scope != null && e.sym.name != sym.name)
446 e = e.shadowed;
447 return e;
448 } 487 }
449 488
450 public Scope getOrigin() { 489 public Scope getOrigin() {
451 // The origin is only recorded for import scopes. For all 490 // The origin is only recorded for import scopes. For all
452 // other scope entries, the "enclosing" type is available 491 // other scope entries, the "enclosing" type is available
454 // Attr.visitIdent. Rather than throwing an assertion 493 // Attr.visitIdent. Rather than throwing an assertion
455 // error, we return scope which will be the same as origin 494 // error, we return scope which will be the same as origin
456 // in many cases. 495 // in many cases.
457 return scope; 496 return scope;
458 } 497 }
498
499 protected boolean isBogus () { return false; }
459 } 500 }
460 501
461 public static class ImportScope extends Scope { 502 public static class ImportScope extends Scope {
462 503
463 public ImportScope(Symbol owner) { 504 public ImportScope(Symbol owner) {
468 Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) { 509 Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
469 return new ImportEntry(sym, shadowed, sibling, scope, origin); 510 return new ImportEntry(sym, shadowed, sibling, scope, origin);
470 } 511 }
471 512
472 public Entry lookup(Name name) { 513 public Entry lookup(Name name) {
473 Entry e = table[name.hashCode() & hashMask]; 514 Entry e = table[getIndex(name)];
474 while (e.scope != null && 515 if (e == null)
475 (e.sym.name != name || 516 return sentinel;
476 /* Since an inner class will show up in package and 517 while (e.isBogus())
477 * import scopes until its inner class attribute has
478 * been processed, we have to weed it out here. This
479 * is done by comparing the owners of the entry's
480 * scope and symbol fields. The scope field's owner
481 * points to where the class originally was imported
482 * from. The symbol field's owner points to where the
483 * class is situated now. This can change when an
484 * inner class is read (see ClassReader.enterClass).
485 * By comparing the two fields we make sure that we do
486 * not accidentally import an inner class that started
487 * life as a flat class in a package. */
488 e.sym.owner != e.scope.owner))
489 e = e.shadowed; 518 e = e.shadowed;
490 return e; 519 return e;
491 } 520 }
492 521
493 static class ImportEntry extends Entry { 522 static class ImportEntry extends Entry {
497 super(sym, shadowed, sibling, scope); 526 super(sym, shadowed, sibling, scope);
498 this.origin = origin; 527 this.origin = origin;
499 } 528 }
500 public Entry next() { 529 public Entry next() {
501 Entry e = super.shadowed; 530 Entry e = super.shadowed;
502 while (e.scope != null && 531 while (isBogus())
503 (e.sym.name != sym.name ||
504 e.sym.owner != e.scope.owner)) // see lookup()
505 e = e.shadowed; 532 e = e.shadowed;
506 return e; 533 return e;
507 } 534 }
508 535
509 @Override 536 @Override
510 public Scope getOrigin() { return origin; } 537 public Scope getOrigin() { return origin; }
538
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; }
511 } 558 }
512 } 559 }
513 560
514 /** An empty scope, into which you can't place anything. Used for 561 /** An empty scope, into which you can't place anything. Used for
515 * the scope for a variable initializer. 562 * the scope for a variable initializer.

mercurial