6980862: too aggressive compiler optimization causes stale results of Types.implementation()
Summary: use a scope counter in order to determine when/if the implementation cache entries are stale
Reviewed-by: jjg
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Scope.java Thu Sep 16 09:57:37 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Scope.java Sat Sep 18 09:54:51 2010 -0700
@@ -70,6 +70,45 @@
*/
public int nelems = 0;
+ /** A timestamp - useful to quickly check whether a scope has changed or not
+ */
+ public ScopeCounter scopeCounter;
+
+ static ScopeCounter dummyCounter = new ScopeCounter() {
+ @Override
+ public void inc() {
+ //do nothing
+ }
+ };
+
+ public static class ScopeCounter {
+ protected static final Context.Key<ScopeCounter> scopeCounterKey =
+ new Context.Key<ScopeCounter>();
+
+ public static ScopeCounter instance(Context context) {
+ ScopeCounter instance = context.get(scopeCounterKey);
+ if (instance == null)
+ instance = new ScopeCounter(context);
+ return instance;
+ }
+
+ protected ScopeCounter(Context context) {
+ context.put(scopeCounterKey, this);
+ }
+
+ private ScopeCounter() {};
+
+ private long val = 0;
+
+ public void inc() {
+ val++;
+ }
+
+ public long val() {
+ return val;
+ }
+ }
+
/** Every hash bucket is a list of Entry's which ends in sentinel.
*/
private static final Entry sentinel = new Entry(null, null, null, null);
@@ -80,12 +119,12 @@
/** A value for the empty scope.
*/
- public static final Scope emptyScope = new Scope(null, null, new Entry[]{});
+ public static final Scope emptyScope = new Scope(null, null, new Entry[]{}, dummyCounter);
/** Construct a new scope, within scope next, with given owner, using
* given table. The table's length must be an exponent of 2.
*/
- Scope(Scope next, Symbol owner, Entry[] table) {
+ private Scope(Scope next, Symbol owner, Entry[] table, ScopeCounter scopeCounter) {
this.next = next;
assert emptyScope == null || owner != null;
this.owner = owner;
@@ -94,13 +133,18 @@
this.elems = null;
this.nelems = 0;
this.shared = 0;
+ this.scopeCounter = scopeCounter;
}
/** Construct a new scope, within scope next, with given owner,
* using a fresh table of length INITIAL_SIZE.
*/
public Scope(Symbol owner) {
- this(null, owner, new Entry[INITIAL_SIZE]);
+ this(owner, dummyCounter);
+ }
+
+ protected Scope(Symbol owner, ScopeCounter scopeCounter) {
+ this(null, owner, new Entry[INITIAL_SIZE], scopeCounter);
for (int i = 0; i < INITIAL_SIZE; i++) table[i] = sentinel;
}
@@ -110,7 +154,7 @@
* of fresh tables.
*/
public Scope dup() {
- Scope result = new Scope(this, this.owner, this.table);
+ Scope result = new Scope(this, this.owner, this.table, scopeCounter);
shared++;
// System.out.println("====> duping scope " + this.hashCode() + " owned by " + this.owner + " to " + result.hashCode());
// new Error().printStackTrace(System.out);
@@ -123,7 +167,7 @@
* of fresh tables.
*/
public Scope dup(Symbol newOwner) {
- Scope result = new Scope(this, newOwner, this.table);
+ Scope result = new Scope(this, newOwner, this.table, scopeCounter);
shared++;
// System.out.println("====> duping scope " + this.hashCode() + " owned by " + newOwner + " to " + result.hashCode());
// new Error().printStackTrace(System.out);
@@ -135,7 +179,7 @@
* the table of its outer scope.
*/
public Scope dupUnshared() {
- return new Scope(this, this.owner, this.table.clone());
+ return new Scope(this, this.owner, this.table.clone(), scopeCounter);
}
/** Remove all entries of this scope from its table, if shared
@@ -211,6 +255,7 @@
table[hash] = e;
elems = e;
nelems++;
+ scopeCounter.inc();
}
Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
@@ -226,6 +271,8 @@
while (e.scope == this && e.sym != sym) e = e.next();
if (e.scope == null) return;
+ scopeCounter.inc();
+
// remove e from table and shadowed list;
Entry te = table[sym.name.hashCode() & hashMask];
if (te == e)
@@ -472,7 +519,7 @@
public static final Entry[] emptyTable = new Entry[0];
public DelegatedScope(Scope outer) {
- super(outer, outer.owner, emptyTable);
+ super(outer, outer.owner, emptyTable, outer.scopeCounter);
delegatee = outer;
}
public Scope dup() {
@@ -498,10 +545,22 @@
}
}
+ /** A class scope, for which a scope counter should be provided */
+ public static class ClassScope extends Scope {
+
+ ClassScope(Scope next, Symbol owner, Entry[] table, ScopeCounter scopeCounter) {
+ super(next, owner, table, scopeCounter);
+ }
+
+ public ClassScope(Symbol owner, ScopeCounter scopeCounter) {
+ super(owner, scopeCounter);
+ }
+ }
+
/** An error scope, for which the owner should be an error symbol. */
public static class ErrorScope extends Scope {
ErrorScope(Scope next, Symbol errSymbol, Entry[] table) {
- super(next, /*owner=*/errSymbol, table);
+ super(next, /*owner=*/errSymbol, table, dummyCounter);
}
public ErrorScope(Symbol errSymbol) {
super(errSymbol);
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java Thu Sep 16 09:57:37 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Symtab.java Sat Sep 18 09:54:51 2010 -0700
@@ -74,6 +74,7 @@
public final JCNoType voidType = new JCNoType(TypeTags.VOID);
private final Names names;
+ private final Scope.ScopeCounter scopeCounter;
private final ClassReader reader;
private final Target target;
@@ -340,6 +341,7 @@
context.put(symtabKey, this);
names = Names.instance(context);
+ scopeCounter = Scope.ScopeCounter.instance(context);
target = Target.instance(context);
// Create the unknown type
@@ -386,7 +388,7 @@
// Create class to hold all predefined constants and operations.
predefClass = new ClassSymbol(PUBLIC|ACYCLIC, names.empty, rootPackage);
- Scope scope = new Scope(predefClass);
+ Scope scope = new Scope.ClassScope(predefClass, scopeCounter);
predefClass.members_field = scope;
// Enter symbols for basic types.
@@ -476,7 +478,7 @@
proprietarySymbol.completer = null;
proprietarySymbol.flags_field = PUBLIC|ACYCLIC|ANNOTATION|INTERFACE;
proprietarySymbol.erasure_field = proprietaryType;
- proprietarySymbol.members_field = new Scope(proprietarySymbol);
+ proprietarySymbol.members_field = new Scope.ClassScope(proprietarySymbol, scopeCounter);
proprietaryType.typarams_field = List.nil();
proprietaryType.allparams_field = List.nil();
proprietaryType.supertype_field = annotationType;
@@ -488,7 +490,7 @@
ClassType arrayClassType = (ClassType)arrayClass.type;
arrayClassType.supertype_field = objectType;
arrayClassType.interfaces_field = List.of(cloneableType, serializableType);
- arrayClass.members_field = new Scope(arrayClass);
+ arrayClass.members_field = new Scope.ClassScope(arrayClass, scopeCounter);
lengthVar = new VarSymbol(
PUBLIC | FINAL,
names.length,
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Types.java Thu Sep 16 09:57:37 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Types.java Sat Sep 18 09:54:51 2010 -0700
@@ -69,6 +69,7 @@
new Context.Key<Types>();
final Symtab syms;
+ final Scope.ScopeCounter scopeCounter;
final JavacMessages messages;
final Names names;
final boolean allowBoxing;
@@ -89,6 +90,7 @@
protected Types(Context context) {
context.put(typesKey, this);
syms = Symtab.instance(context);
+ scopeCounter = Scope.ScopeCounter.instance(context);
names = Names.instance(context);
allowBoxing = Source.instance(context).allowBoxing();
reader = ClassReader.instance(context);
@@ -1984,22 +1986,26 @@
final MethodSymbol cachedImpl;
final Filter<Symbol> implFilter;
final boolean checkResult;
+ final Scope.ScopeCounter scopeCounter;
public Entry(MethodSymbol cachedImpl,
Filter<Symbol> scopeFilter,
- boolean checkResult) {
+ boolean checkResult,
+ Scope.ScopeCounter scopeCounter) {
this.cachedImpl = cachedImpl;
this.implFilter = scopeFilter;
this.checkResult = checkResult;
+ this.scopeCounter = scopeCounter;
}
- boolean matches(Filter<Symbol> scopeFilter, boolean checkResult) {
+ boolean matches(Filter<Symbol> scopeFilter, boolean checkResult, Scope.ScopeCounter scopeCounter) {
return this.implFilter == scopeFilter &&
- this.checkResult == checkResult;
+ this.checkResult == checkResult &&
+ this.scopeCounter.val() >= scopeCounter.val();
}
}
- MethodSymbol get(MethodSymbol ms, TypeSymbol origin, boolean checkResult, Filter<Symbol> implFilter) {
+ MethodSymbol get(MethodSymbol ms, TypeSymbol origin, boolean checkResult, Filter<Symbol> implFilter, Scope.ScopeCounter scopeCounter) {
SoftReference<Map<TypeSymbol, Entry>> ref_cache = _map.get(ms);
Map<TypeSymbol, Entry> cache = ref_cache != null ? ref_cache.get() : null;
if (cache == null) {
@@ -2008,9 +2014,9 @@
}
Entry e = cache.get(origin);
if (e == null ||
- !e.matches(implFilter, checkResult)) {
+ !e.matches(implFilter, checkResult, scopeCounter)) {
MethodSymbol impl = implementationInternal(ms, origin, Types.this, checkResult, implFilter);
- cache.put(origin, new Entry(impl, implFilter, checkResult));
+ cache.put(origin, new Entry(impl, implFilter, checkResult, scopeCounter));
return impl;
}
else {
@@ -2038,7 +2044,7 @@
private ImplementationCache implCache = new ImplementationCache();
public MethodSymbol implementation(MethodSymbol ms, TypeSymbol origin, Types types, boolean checkResult, Filter<Symbol> implFilter) {
- return implCache.get(ms, origin, checkResult, implFilter);
+ return implCache.get(ms, origin, checkResult, implFilter, scopeCounter);
}
// </editor-fold>
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Enter.java Thu Sep 16 09:57:37 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Enter.java Sat Sep 18 09:54:51 2010 -0700
@@ -94,6 +94,7 @@
Log log;
Symtab syms;
+ Scope.ScopeCounter scopeCounter;
Check chk;
TreeMaker make;
ClassReader reader;
@@ -121,6 +122,7 @@
reader = ClassReader.instance(context);
make = TreeMaker.instance(context);
syms = Symtab.instance(context);
+ scopeCounter = Scope.ScopeCounter.instance(context);
chk = Check.instance(context);
memberEnter = MemberEnter.instance(context);
types = Types.instance(context);
@@ -189,7 +191,7 @@
*/
public Env<AttrContext> classEnv(JCClassDecl tree, Env<AttrContext> env) {
Env<AttrContext> localEnv =
- env.dup(tree, env.info.dup(new Scope(tree.sym)));
+ env.dup(tree, env.info.dup(new Scope.ClassScope(tree.sym, scopeCounter)));
localEnv.enclClass = tree;
localEnv.outer = env;
localEnv.info.isSelfCall = false;
@@ -325,7 +327,7 @@
c.flatname = names.fromString(tree.packge + "." + name);
c.sourcefile = tree.sourcefile;
c.completer = null;
- c.members_field = new Scope(c);
+ c.members_field = new Scope.ClassScope(c, scopeCounter);
tree.packge.package_info = c;
}
classEnter(tree.defs, topEnv);
@@ -393,7 +395,7 @@
c.completer = memberEnter;
c.flags_field = chk.checkFlags(tree.pos(), tree.mods.flags, c, tree);
c.sourcefile = env.toplevel.sourcefile;
- c.members_field = new Scope(c);
+ c.members_field = new Scope.ClassScope(c, scopeCounter);
ClassType ct = (ClassType)c.type;
if (owner.kind != PCK && (c.flags_field & STATIC) == 0) {
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java Thu Sep 16 09:57:37 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Lower.java Sat Sep 18 09:54:51 2010 -0700
@@ -68,6 +68,7 @@
private Names names;
private Log log;
private Symtab syms;
+ private Scope.ScopeCounter scopeCounter;
private Resolve rs;
private Check chk;
private Attr attr;
@@ -90,6 +91,7 @@
names = Names.instance(context);
log = Log.instance(context);
syms = Symtab.instance(context);
+ scopeCounter = Scope.ScopeCounter.instance(context);
rs = Resolve.instance(context);
chk = Check.instance(context);
attr = Attr.instance(context);
@@ -569,7 +571,7 @@
c.flatname = chk.localClassName(c);
c.sourcefile = owner.sourcefile;
c.completer = null;
- c.members_field = new Scope(c);
+ c.members_field = new Scope.ClassScope(c, scopeCounter);
c.flags_field = flags;
ClassType ctype = (ClassType) c.type;
ctype.supertype_field = syms.objectType;
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java Thu Sep 16 09:57:37 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java Sat Sep 18 09:54:51 2010 -0700
@@ -67,6 +67,7 @@
private final Check chk;
private final Attr attr;
private final Symtab syms;
+ private final Scope.ScopeCounter scopeCounter;
private final TreeMaker make;
private final ClassReader reader;
private final Todo todo;
@@ -92,6 +93,7 @@
chk = Check.instance(context);
attr = Attr.instance(context);
syms = Symtab.instance(context);
+ scopeCounter = Scope.ScopeCounter.instance(context);
make = TreeMaker.instance(context);
reader = ClassReader.instance(context);
todo = Todo.instance(context);
@@ -1087,7 +1089,7 @@
private Env<AttrContext> baseEnv(JCClassDecl tree, Env<AttrContext> env) {
- Scope baseScope = new Scope(tree.sym);
+ Scope baseScope = new Scope.ClassScope(tree.sym, scopeCounter);
//import already entered local classes into base scope
for (Scope.Entry e = env.outer.info.scope.elems ; e != null ; e = e.sibling) {
if (e.sym.isLocal()) {
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Thu Sep 16 09:57:37 2010 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Sat Sep 18 09:54:51 2010 -0700
@@ -122,6 +122,9 @@
/** The symbol table. */
Symtab syms;
+ /** The scope counter */
+ Scope.ScopeCounter scopeCounter;
+
Types types;
/** The name table. */
@@ -244,6 +247,7 @@
names = Names.instance(context);
syms = Symtab.instance(context);
+ scopeCounter = Scope.ScopeCounter.instance(context);
types = Types.instance(context);
fileManager = context.get(JavaFileManager.class);
if (fileManager == null)
@@ -1984,7 +1988,7 @@
ClassType ct = (ClassType)c.type;
// allocate scope for members
- c.members_field = new Scope(c);
+ c.members_field = new Scope.ClassScope(c, scopeCounter);
// prepare type variable table
typevars = typevars.dup(currentOwner);