Thu, 30 Sep 2010 10:47:12 -0700
6988436: Cleanup javac option handling
Reviewed-by: darcy
1 /*
2 * Copyright (c) 1999, 2008, 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. 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 *
37 * <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 * This code and its internal interfaces are subject to change or
40 * deletion without notice.</b>
41 */
42 public class Scope {
44 /** The number of scopes that share this scope's hash table.
45 */
46 private int shared;
48 /** Next enclosing scope (with whom this scope may share a hashtable)
49 */
50 public Scope next;
52 /** The scope's owner.
53 */
54 public Symbol owner;
56 /** A hash table for the scope's entries.
57 */
58 public Entry[] table;
60 /** Mask for hash codes, always equal to (table.length - 1).
61 */
62 int hashMask;
64 /** A linear list that also contains all entries in
65 * reverse order of appearance (i.e later entries are pushed on top).
66 */
67 public Entry elems;
69 /** The number of elements in this scope.
70 */
71 public int nelems = 0;
73 /** A timestamp - useful to quickly check whether a scope has changed or not
74 */
75 public ScopeCounter scopeCounter;
77 static ScopeCounter dummyCounter = new ScopeCounter() {
78 @Override
79 public void inc() {
80 //do nothing
81 }
82 };
84 public static class ScopeCounter {
85 protected static final Context.Key<ScopeCounter> scopeCounterKey =
86 new Context.Key<ScopeCounter>();
88 public static ScopeCounter instance(Context context) {
89 ScopeCounter instance = context.get(scopeCounterKey);
90 if (instance == null)
91 instance = new ScopeCounter(context);
92 return instance;
93 }
95 protected ScopeCounter(Context context) {
96 context.put(scopeCounterKey, this);
97 }
99 private ScopeCounter() {};
101 private long val = 0;
103 public void inc() {
104 val++;
105 }
107 public long val() {
108 return val;
109 }
110 }
112 /** Every hash bucket is a list of Entry's which ends in sentinel.
113 */
114 private static final Entry sentinel = new Entry(null, null, null, null);
116 /** The hash table's initial size.
117 */
118 private static final int INITIAL_SIZE = 0x10;
120 /** A value for the empty scope.
121 */
122 public static final Scope emptyScope = new Scope(null, null, new Entry[]{}, dummyCounter);
124 /** Construct a new scope, within scope next, with given owner, using
125 * given table. The table's length must be an exponent of 2.
126 */
127 private Scope(Scope next, Symbol owner, Entry[] table, ScopeCounter scopeCounter) {
128 this.next = next;
129 assert emptyScope == null || owner != null;
130 this.owner = owner;
131 this.table = table;
132 this.hashMask = table.length - 1;
133 this.elems = null;
134 this.nelems = 0;
135 this.shared = 0;
136 this.scopeCounter = scopeCounter;
137 }
139 /** Construct a new scope, within scope next, with given owner,
140 * using a fresh table of length INITIAL_SIZE.
141 */
142 public Scope(Symbol owner) {
143 this(owner, dummyCounter);
144 }
146 protected Scope(Symbol owner, ScopeCounter scopeCounter) {
147 this(null, owner, new Entry[INITIAL_SIZE], scopeCounter);
148 for (int i = 0; i < INITIAL_SIZE; i++) table[i] = sentinel;
149 }
151 /** Construct a fresh scope within this scope, with same owner,
152 * 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
154 * of fresh tables.
155 */
156 public Scope dup() {
157 Scope result = new Scope(this, this.owner, this.table, scopeCounter);
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 }
164 /** Construct a fresh scope within this scope, with new owner,
165 * 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 * of fresh tables.
168 */
169 public Scope dup(Symbol newOwner) {
170 Scope result = new Scope(this, newOwner, this.table, scopeCounter);
171 shared++;
172 // System.out.println("====> duping scope " + this.hashCode() + " owned by " + newOwner + " to " + result.hashCode());
173 // new Error().printStackTrace(System.out);
174 return result;
175 }
177 /** Construct a fresh scope within this scope, with same owner,
178 * with a new hash table, whose contents initially are those of
179 * the table of its outer scope.
180 */
181 public Scope dupUnshared() {
182 return new Scope(this, this.owner, this.table.clone(), scopeCounter);
183 }
185 /** Remove all entries of this scope from its table, if shared
186 * with next.
187 */
188 public Scope leave() {
189 assert shared == 0;
190 if (table != next.table) return next;
191 while (elems != null) {
192 int hash = elems.sym.name.hashCode() & hashMask;
193 Entry e = table[hash];
194 assert e == elems : elems.sym;
195 table[hash] = elems.shadowed;
196 elems = elems.sibling;
197 }
198 assert next.shared > 0;
199 next.shared--;
200 // System.out.println("====> leaving scope " + this.hashCode() + " owned by " + this.owner + " to " + next.hashCode());
201 // new Error().printStackTrace(System.out);
202 return next;
203 }
205 /** Double size of hash table.
206 */
207 private void dble() {
208 assert shared == 0;
209 Entry[] oldtable = table;
210 Entry[] newtable = new Entry[oldtable.length * 2];
211 for (Scope s = this; s != null; s = s.next) {
212 if (s.table == oldtable) {
213 assert s == this || s.shared != 0;
214 s.table = newtable;
215 s.hashMask = newtable.length - 1;
216 }
217 }
218 for (int i = 0; i < newtable.length; i++) newtable[i] = sentinel;
219 for (int i = 0; i < oldtable.length; i++) copy(oldtable[i]);
220 }
222 /** Copy the given entry and all entries shadowed by it to table
223 */
224 private void copy(Entry e) {
225 if (e.sym != null) {
226 copy(e.shadowed);
227 int hash = e.sym.name.hashCode() & hashMask;
228 e.shadowed = table[hash];
229 table[hash] = e;
230 }
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 // Temporarily disabled (bug 6460352):
252 // if (nelems * 3 >= hashMask * 2) dble();
253 int hash = sym.name.hashCode() & hashMask;
254 Entry e = makeEntry(sym, table[hash], elems, s, origin);
255 table[hash] = e;
256 elems = e;
257 nelems++;
258 scopeCounter.inc();
259 }
261 Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
262 return new Entry(sym, shadowed, sibling, scope);
263 }
265 /** Remove symbol from this scope. Used when an inner class
266 * attribute tells us that the class isn't a package member.
267 */
268 public void remove(Symbol sym) {
269 assert shared == 0;
270 Entry e = lookup(sym.name);
271 while (e.scope == this && e.sym != sym) e = e.next();
272 if (e.scope == null) return;
274 scopeCounter.inc();
276 // remove e from table and shadowed list;
277 Entry te = table[sym.name.hashCode() & hashMask];
278 if (te == e)
279 table[sym.name.hashCode() & hashMask] = e.shadowed;
280 else while (true) {
281 if (te.shadowed == e) {
282 te.shadowed = e.shadowed;
283 break;
284 }
285 te = te.shadowed;
286 }
288 // remove e from elems and sibling list
289 te = elems;
290 if (te == e)
291 elems = e.sibling;
292 else while (true) {
293 if (te.sibling == e) {
294 te.sibling = e.sibling;
295 break;
296 }
297 te = te.sibling;
298 }
299 }
301 /** Enter symbol sym in this scope if not already there.
302 */
303 public void enterIfAbsent(Symbol sym) {
304 assert shared == 0;
305 Entry e = lookup(sym.name);
306 while (e.scope == this && e.sym.kind != sym.kind) e = e.next();
307 if (e.scope != this) enter(sym);
308 }
310 /** Given a class, is there already a class with same fully
311 * qualified name in this (import) scope?
312 */
313 public boolean includes(Symbol c) {
314 for (Scope.Entry e = lookup(c.name);
315 e.scope == this;
316 e = e.next()) {
317 if (e.sym == c) return true;
318 }
319 return false;
320 }
322 static final Filter<Symbol> noFilter = new Filter<Symbol>() {
323 public boolean accepts(Symbol s) {
324 return true;
325 }
326 };
328 /** Return the entry associated with given name, starting in
329 * this scope and proceeding outwards. If no entry was found,
330 * return the sentinel, which is characterized by having a null in
331 * both its scope and sym fields, whereas both fields are non-null
332 * for regular entries.
333 */
334 public Entry lookup(Name name) {
335 return lookup(name, noFilter);
336 }
337 public Entry lookup(Name name, Filter<Symbol> sf) {
338 Entry e = table[name.hashCode() & hashMask];
339 while (e.scope != null && (e.sym.name != name || !sf.accepts(e.sym)))
340 e = e.shadowed;
341 return e;
342 }
344 public Iterable<Symbol> getElements() {
345 return getElements(noFilter);
346 }
348 public Iterable<Symbol> getElements(final Filter<Symbol> sf) {
349 return new Iterable<Symbol>() {
350 public Iterator<Symbol> iterator() {
351 return new Iterator<Symbol>() {
352 private Scope currScope = Scope.this;
353 private Scope.Entry currEntry = elems;
354 {
355 update();
356 }
358 public boolean hasNext() {
359 return currEntry != null;
360 }
362 public Symbol next() {
363 Symbol sym = (currEntry == null ? null : currEntry.sym);
364 if (currEntry != null) {
365 currEntry = currEntry.sibling;
366 }
367 update();
368 return sym;
369 }
371 public void remove() {
372 throw new UnsupportedOperationException();
373 }
375 private void update() {
376 skipToNextMatchingEntry();
377 while (currEntry == null && currScope.next != null) {
378 currScope = currScope.next;
379 currEntry = currScope.elems;
380 skipToNextMatchingEntry();
381 }
382 }
384 void skipToNextMatchingEntry() {
385 while (currEntry != null && !sf.accepts(currEntry.sym)) {
386 currEntry = currEntry.sibling;
387 }
388 }
389 };
390 }
391 };
393 }
395 public String toString() {
396 StringBuilder result = new StringBuilder();
397 result.append("Scope[");
398 for (Scope s = this; s != null ; s = s.next) {
399 if (s != this) result.append(" | ");
400 for (Entry e = s.elems; e != null; e = e.sibling) {
401 if (e != s.elems) result.append(", ");
402 result.append(e.sym);
403 }
404 }
405 result.append("]");
406 return result.toString();
407 }
409 /** A class for scope entries.
410 */
411 public static class Entry {
413 /** The referenced symbol.
414 * sym == null iff this == sentinel
415 */
416 public Symbol sym;
418 /** An entry with the same hash code, or sentinel.
419 */
420 private Entry shadowed;
422 /** Next entry in same scope.
423 */
424 public Entry sibling;
426 /** The entry's scope.
427 * scope == null iff this == sentinel
428 * for an entry in an import scope, this is the scope
429 * where the entry came from (i.e. was imported from).
430 */
431 public Scope scope;
433 public Entry(Symbol sym, Entry shadowed, Entry sibling, Scope scope) {
434 this.sym = sym;
435 this.shadowed = shadowed;
436 this.sibling = sibling;
437 this.scope = scope;
438 }
440 /** Return next entry with the same name as this entry, proceeding
441 * outwards if not found in this scope.
442 */
443 public Entry next() {
444 Entry e = shadowed;
445 while (e.scope != null && e.sym.name != sym.name)
446 e = e.shadowed;
447 return e;
448 }
450 public Scope getOrigin() {
451 // The origin is only recorded for import scopes. For all
452 // other scope entries, the "enclosing" type is available
453 // from other sources. See Attr.visitSelect and
454 // Attr.visitIdent. Rather than throwing an assertion
455 // error, we return scope which will be the same as origin
456 // in many cases.
457 return scope;
458 }
459 }
461 public static class ImportScope extends Scope {
463 public ImportScope(Symbol owner) {
464 super(owner);
465 }
467 @Override
468 Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
469 return new ImportEntry(sym, shadowed, sibling, scope, origin);
470 }
472 public Entry lookup(Name name) {
473 Entry e = table[name.hashCode() & hashMask];
474 while (e.scope != null &&
475 (e.sym.name != name ||
476 /* Since an inner class will show up in package and
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;
490 return e;
491 }
493 static class ImportEntry extends Entry {
494 private Scope origin;
496 ImportEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
497 super(sym, shadowed, sibling, scope);
498 this.origin = origin;
499 }
500 public Entry next() {
501 Entry e = super.shadowed;
502 while (e.scope != null &&
503 (e.sym.name != sym.name ||
504 e.sym.owner != e.scope.owner)) // see lookup()
505 e = e.shadowed;
506 return e;
507 }
509 @Override
510 public Scope getOrigin() { return origin; }
511 }
512 }
514 /** An empty scope, into which you can't place anything. Used for
515 * the scope for a variable initializer.
516 */
517 public static class DelegatedScope extends Scope {
518 Scope delegatee;
519 public static final Entry[] emptyTable = new Entry[0];
521 public DelegatedScope(Scope outer) {
522 super(outer, outer.owner, emptyTable, outer.scopeCounter);
523 delegatee = outer;
524 }
525 public Scope dup() {
526 return new DelegatedScope(next);
527 }
528 public Scope dupUnshared() {
529 return new DelegatedScope(next);
530 }
531 public Scope leave() {
532 return next;
533 }
534 public void enter(Symbol sym) {
535 // only anonymous classes could be put here
536 }
537 public void enter(Symbol sym, Scope s) {
538 // only anonymous classes could be put here
539 }
540 public void remove(Symbol sym) {
541 throw new AssertionError(sym);
542 }
543 public Entry lookup(Name name) {
544 return delegatee.lookup(name);
545 }
546 }
548 /** A class scope, for which a scope counter should be provided */
549 public static class ClassScope extends Scope {
551 ClassScope(Scope next, Symbol owner, Entry[] table, ScopeCounter scopeCounter) {
552 super(next, owner, table, scopeCounter);
553 }
555 public ClassScope(Symbol owner, ScopeCounter scopeCounter) {
556 super(owner, scopeCounter);
557 }
558 }
560 /** An error scope, for which the owner should be an error symbol. */
561 public static class ErrorScope extends Scope {
562 ErrorScope(Scope next, Symbol errSymbol, Entry[] table) {
563 super(next, /*owner=*/errSymbol, table, dummyCounter);
564 }
565 public ErrorScope(Symbol errSymbol) {
566 super(errSymbol);
567 }
568 public Scope dup() {
569 return new ErrorScope(this, owner, table);
570 }
571 public Scope dupUnshared() {
572 return new ErrorScope(this, owner, table.clone());
573 }
574 public Entry lookup(Name name) {
575 Entry e = super.lookup(name);
576 if (e.scope == null)
577 return new Entry(owner, null, null, null);
578 else
579 return e;
580 }
581 }
582 }