mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 02:54:35 +02:00
7017664: Add listeners infrastracture to javac scopes
Add listeners to javac scopes, added CompoundScope and correct invalidation logic for ImplementationCache Reviewed-by: jjg
This commit is contained in:
parent
511e9c3dcb
commit
77f10a1c5f
6 changed files with 575 additions and 51 deletions
|
@ -74,7 +74,7 @@ public class Scope {
|
||||||
|
|
||||||
/** A list of scopes to be notified if items are to be removed from this scope.
|
/** A list of scopes to be notified if items are to be removed from this scope.
|
||||||
*/
|
*/
|
||||||
List<Scope> listeners = List.nil();
|
List<ScopeListener> listeners = List.nil();
|
||||||
|
|
||||||
/** Use as a "not-found" result for lookup.
|
/** Use as a "not-found" result for lookup.
|
||||||
* Also used to mark deleted entries in the table.
|
* Also used to mark deleted entries in the table.
|
||||||
|
@ -219,12 +219,27 @@ public class Scope {
|
||||||
Entry e = makeEntry(sym, old, elems, s, origin);
|
Entry e = makeEntry(sym, old, elems, s, origin);
|
||||||
table[hash] = e;
|
table[hash] = e;
|
||||||
elems = e;
|
elems = e;
|
||||||
|
|
||||||
|
//notify listeners
|
||||||
|
for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) {
|
||||||
|
l.head.symbolAdded(sym, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
|
Entry makeEntry(Symbol sym, Entry shadowed, Entry sibling, Scope scope, Scope origin) {
|
||||||
return new Entry(sym, shadowed, sibling, scope);
|
return new Entry(sym, shadowed, sibling, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface ScopeListener {
|
||||||
|
public void symbolAdded(Symbol sym, Scope s);
|
||||||
|
public void symbolRemoved(Symbol sym, Scope s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addScopeListener(ScopeListener sl) {
|
||||||
|
listeners = listeners.prepend(sl);
|
||||||
|
}
|
||||||
|
|
||||||
/** Remove symbol from this scope. Used when an inner class
|
/** Remove symbol from this scope. Used when an inner class
|
||||||
* attribute tells us that the class isn't a package member.
|
* attribute tells us that the class isn't a package member.
|
||||||
*/
|
*/
|
||||||
|
@ -258,9 +273,9 @@ public class Scope {
|
||||||
te = te.sibling;
|
te = te.sibling;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove items from scopes that have done importAll
|
//notify listeners
|
||||||
for (List<Scope> l = listeners; l.nonEmpty(); l = l.tail) {
|
for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) {
|
||||||
l.head.remove(sym);
|
l.head.symbolRemoved(sym, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +408,32 @@ public class Scope {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<Symbol> getElementsByName(Name name) {
|
||||||
|
return getElementsByName(name, noFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<Symbol> getElementsByName(final Name name, final Filter<Symbol> sf) {
|
||||||
|
return new Iterable<Symbol>() {
|
||||||
|
public Iterator<Symbol> iterator() {
|
||||||
|
return new Iterator<Symbol>() {
|
||||||
|
Scope.Entry currentEntry = lookup(name, sf);
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return currentEntry.scope != null;
|
||||||
|
}
|
||||||
|
public Symbol next() {
|
||||||
|
Scope.Entry prevEntry = currentEntry;
|
||||||
|
currentEntry = currentEntry.next(sf);
|
||||||
|
return prevEntry.sym;
|
||||||
|
}
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -488,7 +528,7 @@ public class Scope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class StarImportScope extends ImportScope {
|
public static class StarImportScope extends ImportScope implements ScopeListener {
|
||||||
|
|
||||||
public StarImportScope(Symbol owner) {
|
public StarImportScope(Symbol owner) {
|
||||||
super(owner);
|
super(owner);
|
||||||
|
@ -500,8 +540,13 @@ public class Scope {
|
||||||
enter(e.sym, fromScope);
|
enter(e.sym, fromScope);
|
||||||
}
|
}
|
||||||
// Register to be notified when imported items are removed
|
// Register to be notified when imported items are removed
|
||||||
fromScope.listeners = fromScope.listeners.prepend(this);
|
fromScope.addScopeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void symbolRemoved(Symbol sym, Scope s) {
|
||||||
|
remove(sym);
|
||||||
|
}
|
||||||
|
public void symbolAdded(Symbol sym, Scope s) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An empty scope, into which you can't place anything. Used for
|
/** An empty scope, into which you can't place anything. Used for
|
||||||
|
@ -538,6 +583,151 @@ public class Scope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A class scope adds capabilities to keep track of changes in related
|
||||||
|
* class scopes - this allows client to realize whether a class scope
|
||||||
|
* has changed, either directly (because a new member has been added/removed
|
||||||
|
* to this scope) or indirectly (i.e. because a new member has been
|
||||||
|
* added/removed into a supertype scope)
|
||||||
|
*/
|
||||||
|
public static class CompoundScope extends Scope implements ScopeListener {
|
||||||
|
|
||||||
|
public static final Entry[] emptyTable = new Entry[0];
|
||||||
|
|
||||||
|
private List<Scope> subScopes = List.nil();
|
||||||
|
private int mark = 0;
|
||||||
|
|
||||||
|
public CompoundScope(Symbol owner) {
|
||||||
|
super(null, owner, emptyTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSubScope(Scope that) {
|
||||||
|
if (that != null) {
|
||||||
|
subScopes = subScopes.prepend(that);
|
||||||
|
that.addScopeListener(this);
|
||||||
|
mark++;
|
||||||
|
for (ScopeListener sl : listeners) {
|
||||||
|
sl.symbolAdded(null, this); //propagate upwards in case of nested CompoundScopes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void symbolAdded(Symbol sym, Scope s) {
|
||||||
|
mark++;
|
||||||
|
for (ScopeListener sl : listeners) {
|
||||||
|
sl.symbolAdded(sym, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void symbolRemoved(Symbol sym, Scope s) {
|
||||||
|
mark++;
|
||||||
|
for (ScopeListener sl : listeners) {
|
||||||
|
sl.symbolRemoved(sym, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMark() {
|
||||||
|
return mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
buf.append("CompoundScope{");
|
||||||
|
String sep = "";
|
||||||
|
for (Scope s : subScopes) {
|
||||||
|
buf.append(sep);
|
||||||
|
buf.append(s);
|
||||||
|
sep = ",";
|
||||||
|
}
|
||||||
|
buf.append("}");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Symbol> getElements(final Filter<Symbol> sf) {
|
||||||
|
return new Iterable<Symbol>() {
|
||||||
|
public Iterator<Symbol> iterator() {
|
||||||
|
return new CompoundScopeIterator(subScopes) {
|
||||||
|
Iterator<Symbol> nextIterator(Scope s) {
|
||||||
|
return s.getElements().iterator();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Symbol> getElementsByName(final Name name, final Filter<Symbol> sf) {
|
||||||
|
return new Iterable<Symbol>() {
|
||||||
|
public Iterator<Symbol> iterator() {
|
||||||
|
return new CompoundScopeIterator(subScopes) {
|
||||||
|
Iterator<Symbol> nextIterator(Scope s) {
|
||||||
|
return s.getElementsByName(name, sf).iterator();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CompoundScopeIterator implements Iterator<Symbol> {
|
||||||
|
|
||||||
|
private Iterator<Symbol> currentIterator;
|
||||||
|
private List<Scope> scopesToScan;
|
||||||
|
|
||||||
|
public CompoundScopeIterator(List<Scope> scopesToScan) {
|
||||||
|
this.scopesToScan = scopesToScan;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract Iterator<Symbol> nextIterator(Scope s);
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
return currentIterator != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Symbol next() {
|
||||||
|
Symbol sym = currentIterator.next();
|
||||||
|
if (!currentIterator.hasNext()) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
while (scopesToScan.nonEmpty()) {
|
||||||
|
currentIterator = nextIterator(scopesToScan.head);
|
||||||
|
scopesToScan = scopesToScan.tail;
|
||||||
|
if (currentIterator.hasNext()) return;
|
||||||
|
}
|
||||||
|
currentIterator = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry lookup(Name name, Filter<Symbol> sf) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Scope dup(Symbol newOwner) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enter(Symbol sym, Scope s, Scope origin) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Symbol sym) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** An error scope, for which the owner should be an error symbol. */
|
/** An error scope, for which the owner should be an error symbol. */
|
||||||
public static class ErrorScope extends Scope {
|
public static class ErrorScope extends Scope {
|
||||||
ErrorScope(Scope next, Symbol errSymbol, Entry[] table) {
|
ErrorScope(Scope next, Symbol errSymbol, Entry[] table) {
|
||||||
|
|
|
@ -731,7 +731,7 @@ public abstract class Symbol implements Element {
|
||||||
|
|
||||||
/** members closure cache (set by Types.membersClosure)
|
/** members closure cache (set by Types.membersClosure)
|
||||||
*/
|
*/
|
||||||
Scope membersClosure;
|
Scope.CompoundScope membersClosure;
|
||||||
|
|
||||||
public ClassSymbol(long flags, Name name, Type type, Symbol owner) {
|
public ClassSymbol(long flags, Name name, Type type, Symbol owner) {
|
||||||
super(flags, name, type, owner);
|
super(flags, name, type, owner);
|
||||||
|
|
|
@ -2023,18 +2023,22 @@ public class Types {
|
||||||
final MethodSymbol cachedImpl;
|
final MethodSymbol cachedImpl;
|
||||||
final Filter<Symbol> implFilter;
|
final Filter<Symbol> implFilter;
|
||||||
final boolean checkResult;
|
final boolean checkResult;
|
||||||
|
final int prevMark;
|
||||||
|
|
||||||
public Entry(MethodSymbol cachedImpl,
|
public Entry(MethodSymbol cachedImpl,
|
||||||
Filter<Symbol> scopeFilter,
|
Filter<Symbol> scopeFilter,
|
||||||
boolean checkResult) {
|
boolean checkResult,
|
||||||
|
int prevMark) {
|
||||||
this.cachedImpl = cachedImpl;
|
this.cachedImpl = cachedImpl;
|
||||||
this.implFilter = scopeFilter;
|
this.implFilter = scopeFilter;
|
||||||
this.checkResult = checkResult;
|
this.checkResult = checkResult;
|
||||||
|
this.prevMark = prevMark;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean matches(Filter<Symbol> scopeFilter, boolean checkResult) {
|
boolean matches(Filter<Symbol> scopeFilter, boolean checkResult, int mark) {
|
||||||
return this.implFilter == scopeFilter &&
|
return this.implFilter == scopeFilter &&
|
||||||
this.checkResult == checkResult;
|
this.checkResult == checkResult &&
|
||||||
|
this.prevMark == mark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2046,10 +2050,11 @@ public class Types {
|
||||||
_map.put(ms, new SoftReference<Map<TypeSymbol, Entry>>(cache));
|
_map.put(ms, new SoftReference<Map<TypeSymbol, Entry>>(cache));
|
||||||
}
|
}
|
||||||
Entry e = cache.get(origin);
|
Entry e = cache.get(origin);
|
||||||
|
CompoundScope members = membersClosure(origin.type);
|
||||||
if (e == null ||
|
if (e == null ||
|
||||||
!e.matches(implFilter, checkResult)) {
|
!e.matches(implFilter, checkResult, members.getMark())) {
|
||||||
MethodSymbol impl = implementationInternal(ms, origin, Types.this, checkResult, implFilter);
|
MethodSymbol impl = implementationInternal(ms, origin, checkResult, implFilter);
|
||||||
cache.put(origin, new Entry(impl, implFilter, checkResult));
|
cache.put(origin, new Entry(impl, implFilter, checkResult, members.getMark()));
|
||||||
return impl;
|
return impl;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2057,8 +2062,8 @@ public class Types {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodSymbol implementationInternal(MethodSymbol ms, TypeSymbol origin, Types types, boolean checkResult, Filter<Symbol> implFilter) {
|
private MethodSymbol implementationInternal(MethodSymbol ms, TypeSymbol origin, boolean checkResult, Filter<Symbol> implFilter) {
|
||||||
for (Type t = origin.type; t.tag == CLASS || t.tag == TYPEVAR; t = types.supertype(t)) {
|
for (Type t = origin.type; t.tag == CLASS || t.tag == TYPEVAR; t = supertype(t)) {
|
||||||
while (t.tag == TYPEVAR)
|
while (t.tag == TYPEVAR)
|
||||||
t = t.getUpperBound();
|
t = t.getUpperBound();
|
||||||
TypeSymbol c = t.tsym;
|
TypeSymbol c = t.tsym;
|
||||||
|
@ -2066,7 +2071,7 @@ public class Types {
|
||||||
e.scope != null;
|
e.scope != null;
|
||||||
e = e.next(implFilter)) {
|
e = e.next(implFilter)) {
|
||||||
if (e.sym != null &&
|
if (e.sym != null &&
|
||||||
e.sym.overrides(ms, origin, types, checkResult))
|
e.sym.overrides(ms, origin, Types.this, checkResult))
|
||||||
return (MethodSymbol)e.sym;
|
return (MethodSymbol)e.sym;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2082,46 +2087,35 @@ public class Types {
|
||||||
// </editor-fold>
|
// </editor-fold>
|
||||||
|
|
||||||
// <editor-fold defaultstate="collapsed" desc="compute transitive closure of all members in given site">
|
// <editor-fold defaultstate="collapsed" desc="compute transitive closure of all members in given site">
|
||||||
public Scope membersClosure(Type site) {
|
public CompoundScope membersClosure(Type site) {
|
||||||
return membersClosure.visit(site);
|
return membersClosure.visit(site);
|
||||||
}
|
}
|
||||||
|
|
||||||
UnaryVisitor<Scope> membersClosure = new UnaryVisitor<Scope>() {
|
UnaryVisitor<CompoundScope> membersClosure = new UnaryVisitor<CompoundScope>() {
|
||||||
|
|
||||||
public Scope visitType(Type t, Void s) {
|
public CompoundScope visitType(Type t, Void s) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Scope visitClassType(ClassType t, Void s) {
|
public CompoundScope visitClassType(ClassType t, Void s) {
|
||||||
ClassSymbol csym = (ClassSymbol)t.tsym;
|
ClassSymbol csym = (ClassSymbol)t.tsym;
|
||||||
if (csym.membersClosure == null) {
|
if (csym.membersClosure == null) {
|
||||||
Scope membersClosure = new Scope(csym);
|
CompoundScope membersClosure = new CompoundScope(csym);
|
||||||
for (Type i : interfaces(t)) {
|
for (Type i : interfaces(t)) {
|
||||||
enterAll(visit(i), membersClosure);
|
membersClosure.addSubScope(visit(i));
|
||||||
}
|
}
|
||||||
enterAll(visit(supertype(t)), membersClosure);
|
membersClosure.addSubScope(visit(supertype(t)));
|
||||||
enterAll(csym.members(), membersClosure);
|
membersClosure.addSubScope(csym.members());
|
||||||
csym.membersClosure = membersClosure;
|
csym.membersClosure = membersClosure;
|
||||||
}
|
}
|
||||||
return csym.membersClosure;
|
return csym.membersClosure;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Scope visitTypeVar(TypeVar t, Void s) {
|
public CompoundScope visitTypeVar(TypeVar t, Void s) {
|
||||||
return visit(t.getUpperBound());
|
return visit(t.getUpperBound());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enterAll(Scope s, Scope to) {
|
|
||||||
if (s == null) return;
|
|
||||||
List<Symbol> syms = List.nil();
|
|
||||||
for (Scope.Entry e = s.elems ; e != null ; e = e.sibling) {
|
|
||||||
syms = syms.prepend(e.sym);
|
|
||||||
}
|
|
||||||
for (Symbol sym : syms) {
|
|
||||||
to.enter(sym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// </editor-fold>
|
// </editor-fold>
|
||||||
|
|
||||||
|
|
|
@ -2106,32 +2106,32 @@ public class Check {
|
||||||
void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
|
void checkOverrideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
|
||||||
ClashFilter cf = new ClashFilter(site);
|
ClashFilter cf = new ClashFilter(site);
|
||||||
//for each method m1 that is a member of 'site'...
|
//for each method m1 that is a member of 'site'...
|
||||||
for (Scope.Entry e1 = types.membersClosure(site).lookup(sym.name, cf) ;
|
for (Symbol s1 : types.membersClosure(site).getElementsByName(sym.name, cf)) {
|
||||||
e1.scope != null ; e1 = e1.next(cf)) {
|
|
||||||
//...find another method m2 that is overridden (directly or indirectly)
|
//...find another method m2 that is overridden (directly or indirectly)
|
||||||
//by method 'sym' in 'site'
|
//by method 'sym' in 'site'
|
||||||
for (Scope.Entry e2 = types.membersClosure(site).lookup(sym.name, cf) ;
|
for (Symbol s2 : types.membersClosure(site).getElementsByName(sym.name, cf)) {
|
||||||
e2.scope != null ; e2 = e2.next(cf)) {
|
if (s1 == s2 || !sym.overrides(s2, site.tsym, types, false)) continue;
|
||||||
if (e1.sym == e2.sym || !sym.overrides(e2.sym, site.tsym, types, false)) continue;
|
|
||||||
//if (i) the signature of 'sym' is not a subsignature of m1 (seen as
|
//if (i) the signature of 'sym' is not a subsignature of m1 (seen as
|
||||||
//a member of 'site') and (ii) m1 has the same erasure as m2, issue an error
|
//a member of 'site') and (ii) m1 has the same erasure as m2, issue an error
|
||||||
if (!types.isSubSignature(sym.type, types.memberType(site, e1.sym)) &&
|
if (!types.isSubSignature(sym.type, types.memberType(site, s1)) &&
|
||||||
types.hasSameArgs(e1.sym.erasure(types), e2.sym.erasure(types))) {
|
types.hasSameArgs(s1.erasure(types), s2.erasure(types))) {
|
||||||
sym.flags_field |= CLASH;
|
sym.flags_field |= CLASH;
|
||||||
String key = e2.sym == sym ?
|
String key = s2 == sym ?
|
||||||
"name.clash.same.erasure.no.override" :
|
"name.clash.same.erasure.no.override" :
|
||||||
"name.clash.same.erasure.no.override.1";
|
"name.clash.same.erasure.no.override.1";
|
||||||
log.error(pos,
|
log.error(pos,
|
||||||
key,
|
key,
|
||||||
sym, sym.location(),
|
sym, sym.location(),
|
||||||
e1.sym, e1.sym.location(),
|
s1, s1.location(),
|
||||||
e2.sym, e2.sym.location());
|
s2, s2.location());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Check that all static methods accessible from 'site' are
|
/** Check that all static methods accessible from 'site' are
|
||||||
* mutually compatible (JLS 8.4.8).
|
* mutually compatible (JLS 8.4.8).
|
||||||
*
|
*
|
||||||
|
@ -2142,16 +2142,15 @@ public class Check {
|
||||||
void checkHideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
|
void checkHideClashes(DiagnosticPosition pos, Type site, MethodSymbol sym) {
|
||||||
ClashFilter cf = new ClashFilter(site);
|
ClashFilter cf = new ClashFilter(site);
|
||||||
//for each method m1 that is a member of 'site'...
|
//for each method m1 that is a member of 'site'...
|
||||||
for (Scope.Entry e = types.membersClosure(site).lookup(sym.name, cf) ;
|
for (Symbol s : types.membersClosure(site).getElementsByName(sym.name, cf)) {
|
||||||
e.scope != null ; e = e.next(cf)) {
|
|
||||||
//if (i) the signature of 'sym' is not a subsignature of m1 (seen as
|
//if (i) the signature of 'sym' is not a subsignature of m1 (seen as
|
||||||
//a member of 'site') and (ii) 'sym' has the same erasure as m1, issue an error
|
//a member of 'site') and (ii) 'sym' has the same erasure as m1, issue an error
|
||||||
if (!types.isSubSignature(sym.type, types.memberType(site, e.sym)) &&
|
if (!types.isSubSignature(sym.type, types.memberType(site, s)) &&
|
||||||
types.hasSameArgs(e.sym.erasure(types), sym.erasure(types))) {
|
types.hasSameArgs(s.erasure(types), sym.erasure(types))) {
|
||||||
log.error(pos,
|
log.error(pos,
|
||||||
"name.clash.same.erasure.no.hide",
|
"name.clash.same.erasure.no.hide",
|
||||||
sym, sym.location(),
|
sym, sym.location(),
|
||||||
e.sym, e.sym.location());
|
s, s.location());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
212
langtools/test/tools/javac/scope/7017664/CompoundScopeTest.java
Normal file
212
langtools/test/tools/javac/scope/7017664/CompoundScopeTest.java
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 7017664
|
||||||
|
* @summary Basher for CompoundScopes
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import com.sun.tools.javac.util.*;
|
||||||
|
import com.sun.tools.javac.code.*;
|
||||||
|
import com.sun.tools.javac.code.Scope.*;
|
||||||
|
import com.sun.tools.javac.code.Symbol.*;
|
||||||
|
import com.sun.tools.javac.file.JavacFileManager;
|
||||||
|
|
||||||
|
public class CompoundScopeTest {
|
||||||
|
public static void main(String... args) throws Exception {
|
||||||
|
new CompoundScopeTest().run(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final int MAX_SYMBOLS_COUNT = 20;
|
||||||
|
static final int PASSES = 10;
|
||||||
|
|
||||||
|
void run(String... args) throws Exception {
|
||||||
|
int count = PASSES;
|
||||||
|
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
String arg = args[i];
|
||||||
|
if (arg.equals("-seed") && (i + 1 < args.length))
|
||||||
|
seed = Long.parseLong(args[++i]);
|
||||||
|
else if(arg.equals("-tests") && (i + 1 < args.length))
|
||||||
|
count = Integer.parseInt(args[++i]);
|
||||||
|
else
|
||||||
|
throw new Exception("unknown arg: " + arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
rgen = new Random(seed);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
Test t = new Test();
|
||||||
|
t.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors > 0)
|
||||||
|
throw new Exception(errors + " errors found");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a message to stderr.
|
||||||
|
*/
|
||||||
|
void log(String msg) {
|
||||||
|
System.err.println(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an error message to stderr.
|
||||||
|
*/
|
||||||
|
void error(String msg) {
|
||||||
|
System.err.println("Error: " + msg);
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Random rgen;
|
||||||
|
long seed = 0;
|
||||||
|
|
||||||
|
int errors;
|
||||||
|
|
||||||
|
/** Class to encapsulate a test run. */
|
||||||
|
class Test {
|
||||||
|
|
||||||
|
List<Symbol> elems = List.nil();
|
||||||
|
Map<Name, List<Symbol>> shadowedMap = new HashMap<Name, List<Symbol>>();
|
||||||
|
|
||||||
|
/** Run the test. */
|
||||||
|
void run() throws Exception {
|
||||||
|
log ("starting test");
|
||||||
|
setup();
|
||||||
|
Scope[] scopes = { createScope(rgen.nextInt(MAX_SYMBOLS_COUNT)),
|
||||||
|
createScope(rgen.nextInt(MAX_SYMBOLS_COUNT)),
|
||||||
|
createScope(rgen.nextInt(MAX_SYMBOLS_COUNT)) };
|
||||||
|
boolean[][] scopeNesting = { {false, true, false, true},
|
||||||
|
{false, true, true, true},
|
||||||
|
{false, false, true, true} };
|
||||||
|
/**
|
||||||
|
* We want to generate (and check) the following compound scopes:
|
||||||
|
* C1 = C(S1, S2, S3)
|
||||||
|
* C2 = C((S1, S2), S3)
|
||||||
|
* C3 = C(S1, (S2, S3))
|
||||||
|
* C3 = C(C(S1, S2, S3))
|
||||||
|
*/
|
||||||
|
for (int i = 0 ; i < 4 ; i ++) {
|
||||||
|
CompoundScope root = new CompoundScope(symtab.noSymbol);
|
||||||
|
CompoundScope sub = new CompoundScope(symtab.noSymbol);
|
||||||
|
boolean subAdded = false;
|
||||||
|
for (int sc = 0 ; sc < 3 ; sc ++) {
|
||||||
|
if (scopeNesting[sc][i]) {
|
||||||
|
sub.addSubScope(scopes[sc]);
|
||||||
|
if (!subAdded) {
|
||||||
|
root.addSubScope(sub);
|
||||||
|
subAdded = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.addSubScope(scopes[sc]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log("testing scope: " + root);
|
||||||
|
checkElems(root);
|
||||||
|
checkShadowed(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a scope containing a given number of synthetic symbols
|
||||||
|
*/
|
||||||
|
Scope createScope(int nelems) {
|
||||||
|
Scope s = new Scope(symtab.noSymbol);
|
||||||
|
for (int i = 0 ; i < nelems ; i++) {
|
||||||
|
Symbol sym = new TypeSymbol(0, names.fromString("s" + i), null, null);
|
||||||
|
s.enter(sym);
|
||||||
|
elems = elems.prepend(sym);
|
||||||
|
List<Symbol> shadowed = shadowedMap.get(sym.name);
|
||||||
|
if (shadowed == null) {
|
||||||
|
shadowed = List.nil();
|
||||||
|
}
|
||||||
|
shadowedMap.put(sym.name, shadowed.prepend(sym));
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup compiler context
|
||||||
|
*/
|
||||||
|
void setup() {
|
||||||
|
log ("setup");
|
||||||
|
context = new Context();
|
||||||
|
JavacFileManager.preRegister(context); // required by ClassReader which is required by Symtab
|
||||||
|
names = Names.instance(context); // Name.Table impls tied to an instance of Names
|
||||||
|
symtab = Symtab.instance(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that CompoundScope.getElements() correctly visits all symbols
|
||||||
|
* in all subscopes (in the correct order)
|
||||||
|
*/
|
||||||
|
void checkElems(CompoundScope cs) {
|
||||||
|
List<Symbol> allSymbols = elems;
|
||||||
|
int count = 0;
|
||||||
|
for (Symbol s : cs.getElements()) {
|
||||||
|
checkSameSymbols(s, allSymbols.head);
|
||||||
|
allSymbols = allSymbols.tail;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count != elems.size()) {
|
||||||
|
error("CompoundScope.getElements() did not returned enough symbols");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that CompoundScope.getElements() correctly visits all symbols
|
||||||
|
* with a given name in all subscopes (in the correct order)
|
||||||
|
*/
|
||||||
|
void checkShadowed(CompoundScope cs) {
|
||||||
|
for (Map.Entry<Name, List<Symbol>> shadowedEntry : shadowedMap.entrySet()) {
|
||||||
|
int count = 0;
|
||||||
|
List<Symbol> shadowed = shadowedEntry.getValue();
|
||||||
|
Name name = shadowedEntry.getKey();
|
||||||
|
for (Symbol s : cs.getElementsByName(name)) {
|
||||||
|
checkSameSymbols(s, shadowed.head);
|
||||||
|
shadowed = shadowed.tail;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count != shadowedEntry.getValue().size()) {
|
||||||
|
error("CompoundScope.lookup() did not returned enough symbols for name " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkSameSymbols(Symbol found, Symbol req) {
|
||||||
|
if (found != req) {
|
||||||
|
error("Symbol mismatch - found : " + found + ":" + found.hashCode() + "\n" +
|
||||||
|
" required : " + req + ":" + req.hashCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context context;
|
||||||
|
Symtab symtab;
|
||||||
|
Names names;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 7017664
|
||||||
|
* @summary Basher for CompoundScopes
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.sun.source.util.JavacTask;
|
||||||
|
import com.sun.tools.javac.code.Symbol;
|
||||||
|
import com.sun.tools.javac.code.Types;
|
||||||
|
import com.sun.tools.javac.file.JavacFileManager;
|
||||||
|
import com.sun.tools.javac.util.Context;
|
||||||
|
|
||||||
|
import com.sun.tools.javac.code.Symbol.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.tools.JavaCompiler;
|
||||||
|
import javax.tools.JavaFileObject;
|
||||||
|
import javax.tools.SimpleJavaFileObject;
|
||||||
|
import javax.tools.ToolProvider;
|
||||||
|
|
||||||
|
import static javax.tools.JavaFileObject.Kind;
|
||||||
|
|
||||||
|
public class ImplementationCacheTest {
|
||||||
|
|
||||||
|
static class SourceFile extends SimpleJavaFileObject {
|
||||||
|
|
||||||
|
final String source = "interface I { void m(); }\n" +
|
||||||
|
"class A implements I { public void m() {} }\n" +
|
||||||
|
"class B extends A { }\n";
|
||||||
|
|
||||||
|
public SourceFile() {
|
||||||
|
super(URI.create("test.java"), Kind.SOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
List<? extends JavaFileObject> files = Arrays.asList(new SourceFile());
|
||||||
|
JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
|
||||||
|
JavacTask ct = (JavacTask)tool.getTask(null, null, null, null, null, files);
|
||||||
|
Context ctx = new Context();
|
||||||
|
JavacFileManager.preRegister(ctx);
|
||||||
|
checkImplementationCache(ct.analyze(), Types.instance(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkImplementationCache(Iterable<? extends Element> elements, Types types) {
|
||||||
|
if (types == null) {
|
||||||
|
throw new AssertionError("problems initializing Types");
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol a = null;
|
||||||
|
Symbol b = null;
|
||||||
|
Symbol i = null;
|
||||||
|
|
||||||
|
for (Element e : elements) {
|
||||||
|
if (e.getSimpleName().contentEquals("A")) {
|
||||||
|
a = (Symbol)e;
|
||||||
|
} else if (e.getSimpleName().contentEquals("B")) {
|
||||||
|
b = (Symbol)e;
|
||||||
|
} else if (e.getSimpleName().contentEquals("I")) {
|
||||||
|
i = (Symbol)e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a == null || b == null || i == null) {
|
||||||
|
throw new AssertionError("missing class");
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodSymbol I_m = null;
|
||||||
|
|
||||||
|
for (Symbol sym : i.members().getElements()) {
|
||||||
|
if (sym.name.contentEquals("m")) {
|
||||||
|
I_m = (MethodSymbol)sym;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (I_m == null) {
|
||||||
|
throw new AssertionError("missing method m() in scope of interface I");
|
||||||
|
}
|
||||||
|
|
||||||
|
Symbol impl = I_m.implementation((TypeSymbol)b, types, true);
|
||||||
|
|
||||||
|
if (impl == null || impl.owner != a) {
|
||||||
|
throw new AssertionError("wrong implementation for m() in B");
|
||||||
|
}
|
||||||
|
|
||||||
|
b.members().enter(I_m.clone(b));
|
||||||
|
|
||||||
|
Symbol newImpl = I_m.implementation((TypeSymbol)b, types, true);
|
||||||
|
|
||||||
|
if (newImpl == impl) {
|
||||||
|
throw new AssertionError("stale implementation for m() in B");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newImpl == null || newImpl.owner != b) {
|
||||||
|
throw new AssertionError("wrong implementation for m() in B");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue