|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.tools.java; |
|
|
|
import java.util.*; |
|
import java.io.OutputStream; |
|
import java.io.PrintStream; |
|
import sun.tools.tree.Context; |
|
import sun.tools.tree.Vset; |
|
import sun.tools.tree.Expression; |
|
import sun.tools.tree.LocalMember; |
|
import sun.tools.tree.UplevelReference; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public |
|
class ClassDefinition implements Constants { |
|
|
|
protected Object source; |
|
protected long where; |
|
protected int modifiers; |
|
protected Identifier localName; |
|
protected ClassDeclaration declaration; |
|
protected IdentifierToken superClassId; |
|
protected IdentifierToken interfaceIds[]; |
|
protected ClassDeclaration superClass; |
|
protected ClassDeclaration interfaces[]; |
|
protected ClassDefinition outerClass; |
|
protected MemberDefinition outerMember; |
|
protected MemberDefinition innerClassMember; |
|
protected MemberDefinition firstMember; |
|
protected MemberDefinition lastMember; |
|
protected boolean resolved; |
|
protected String documentation; |
|
protected boolean error; |
|
protected boolean nestError; |
|
protected UplevelReference references; |
|
protected boolean referencesFrozen; |
|
private Hashtable fieldHash = new Hashtable(31); |
|
private int abstr; |
|
|
|
// Table of local and anonymous classes whose internal names are constructed |
|
// using the current class as a prefix. This is part of a fix for |
|
// bugid 4054523 and 4030421. See also 'Environment.getClassDefinition' |
|
|
|
private Hashtable localClasses = null; |
|
private final int LOCAL_CLASSES_SIZE = 31; |
|
|
|
// The immediately surrounding context in which the class appears. |
|
// Set at the beginning of checking, upon entry to 'SourceClass.checkInternal'. |
|
// Null for classes that are not local or inside a local class. |
|
// At present, this field exists only for the benefit of 'resolveName' as part |
|
|
|
protected Context classContext; |
|
|
|
// The saved class context is now also used in 'SourceClass.getAccessMember'. |
|
|
|
public Context getClassContext() { |
|
return classContext; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
protected ClassDefinition(Object source, long where, ClassDeclaration declaration, |
|
int modifiers, IdentifierToken superClass, IdentifierToken interfaces[]) { |
|
this.source = source; |
|
this.where = where; |
|
this.declaration = declaration; |
|
this.modifiers = modifiers; |
|
this.superClassId = superClass; |
|
this.interfaceIds = interfaces; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final Object getSource() { |
|
return source; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final boolean getError() { |
|
return error; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final void setError() { |
|
this.error = true; |
|
setNestError(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final boolean getNestError() { |
|
// Check to see if our error value is set, or if any of our |
|
// outer classes' error values are set. This will work in |
|
// conjunction with setError(), which sets the error value |
|
// of its outer class, to yield true is any of our nest |
|
// siblings has an error. This addresses bug 4111488: either |
|
// code should be generated for all classes in a nest, or |
|
|
|
return nestError || ((outerClass != null) ? outerClass.getNestError() : false); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final void setNestError() { |
|
this.nestError = true; |
|
if (outerClass != null) { |
|
// If we have an outer class, set it to be erroneous as well. |
|
// This will work in conjunction with getError(), which checks |
|
// the error value of its outer class, to set the whole class |
|
// nest to be erroneous. This address bug 4111488: either |
|
// code should be generated for all classes in a nest, or |
|
|
|
outerClass.setNestError(); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final long getWhere() { |
|
return where; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final ClassDeclaration getClassDeclaration() { |
|
return declaration; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final int getModifiers() { |
|
return modifiers; |
|
} |
|
public final void subModifiers(int mod) { |
|
modifiers &= ~mod; |
|
} |
|
public final void addModifiers(int mod) { |
|
modifiers |= mod; |
|
} |
|
|
|
|
|
protected boolean supersCheckStarted = !(this instanceof sun.tools.javac.SourceClass); |
|
|
|
|
|
|
|
*/ |
|
public final ClassDeclaration getSuperClass() { |
|
/*--- |
|
if (superClass == null && superClassId != null) |
|
throw new CompilerError("getSuperClass "+superClassId); |
|
// There are obscure cases where null is the right answer, |
|
// in order to enable some error reporting later on. |
|
// For example: class T extends T.N { class N { } } |
|
---*/ |
|
|
|
// *** DEBUG *** |
|
|
|
if (!supersCheckStarted) throw new CompilerError("unresolved super"); |
|
|
|
return superClass; |
|
} |
|
|
|
/** |
|
* Get the super class, and resolve names now if necessary. |
|
* |
|
* It is only possible to resolve names at this point if we are |
|
* a source class. The provision of this method at this level |
|
* in the class hierarchy is dubious, but see 'getInnerClass' below. |
|
* All other calls to 'getSuperClass(env)' appear in 'SourceClass'. |
|
* NOTE: An older definition of this method has been moved to |
|
* 'SourceClass', where it overrides this one. |
|
* |
|
* @see #resolveTypeStructure |
|
*/ |
|
|
|
public ClassDeclaration getSuperClass(Environment env) { |
|
return getSuperClass(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final ClassDeclaration getInterfaces()[] { |
|
if (interfaces == null) throw new CompilerError("getInterfaces"); |
|
return interfaces; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final ClassDefinition getOuterClass() { |
|
return outerClass; |
|
} |
|
|
|
|
|
|
|
*/ |
|
protected final void setOuterClass(ClassDefinition outerClass) { |
|
if (this.outerClass != null) throw new CompilerError("setOuterClass"); |
|
this.outerClass = outerClass; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
protected final void setOuterMember(MemberDefinition outerMember) { |
|
|
|
if (isStatic() || !isInnerClass()) throw new CompilerError("setOuterField"); |
|
if (this.outerMember != null) throw new CompilerError("setOuterField"); |
|
this.outerMember = outerMember; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean isInnerClass() { |
|
return outerClass != null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean isMember() { |
|
return outerClass != null && !isLocal(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean isTopLevel() { |
|
return outerClass == null || isStatic() || isInterface(); |
|
} |
|
|
|
/** |
|
* Tell if the class is local or inside a local class, |
|
* which means it cannot be mentioned outside of its file. |
|
*/ |
|
|
|
// The comment above is true only because M_LOCAL is set |
|
// whenever M_ANONYMOUS is. I think it is risky to assume that |
|
// isAnonymous(x) => isLocal(x). |
|
|
|
public final boolean isInsideLocal() { |
|
return isLocal() || |
|
(outerClass != null && outerClass.isInsideLocal()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean isInsideLocalOrAnonymous() { |
|
return isLocal() || isAnonymous () || |
|
(outerClass != null && outerClass.isInsideLocalOrAnonymous()); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Identifier getLocalName() { |
|
if (localName != null) { |
|
return localName; |
|
} |
|
|
|
return getName().getFlatName().getName(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void setLocalName(Identifier name) { |
|
if (isLocal()) { |
|
localName = name; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final MemberDefinition getInnerClassMember() { |
|
if (outerClass == null) |
|
return null; |
|
if (innerClassMember == null) { |
|
|
|
Identifier nm = getName().getFlatName().getName(); |
|
for (MemberDefinition field = outerClass.getFirstMatch(nm); |
|
field != null; field = field.getNextMatch()) { |
|
if (field.isInnerClass()) { |
|
innerClassMember = field; |
|
break; |
|
} |
|
} |
|
if (innerClassMember == null) |
|
throw new CompilerError("getInnerClassField"); |
|
} |
|
return innerClassMember; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public final MemberDefinition findOuterMember() { |
|
return outerMember; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final boolean isStatic() { |
|
return (modifiers & ACC_STATIC) != 0; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final ClassDefinition getTopClass() { |
|
ClassDefinition p, q; |
|
for (p = this; (q = p.outerClass) != null; p = q) |
|
; |
|
return p; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final MemberDefinition getFirstMember() { |
|
return firstMember; |
|
} |
|
public final MemberDefinition getFirstMatch(Identifier name) { |
|
return (MemberDefinition)fieldHash.get(name); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final Identifier getName() { |
|
return declaration.getName(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final Type getType() { |
|
return declaration.getType(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String getDocumentation() { |
|
return documentation; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static boolean containsDeprecated(String documentation) { |
|
if (documentation == null) { |
|
return false; |
|
} |
|
doScan: |
|
for (int scan = 0; |
|
(scan = documentation.indexOf(paraDeprecated, scan)) >= 0; |
|
scan += paraDeprecated.length()) { |
|
// make sure there is only whitespace between this word |
|
|
|
for (int beg = scan-1; beg >= 0; beg--) { |
|
char ch = documentation.charAt(beg); |
|
if (ch == '\n' || ch == '\r') { |
|
break; |
|
} |
|
if (!Character.isSpace(ch)) { |
|
continue doScan; |
|
} |
|
} |
|
|
|
int end = scan+paraDeprecated.length(); |
|
if (end < documentation.length()) { |
|
char ch = documentation.charAt(end); |
|
if (!(ch == '\n' || ch == '\r') && !Character.isSpace(ch)) { |
|
continue doScan; |
|
} |
|
} |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
public final boolean inSamePackage(ClassDeclaration c) { |
|
// find out if the class stored in c is defined in the same |
|
|
|
return inSamePackage(c.getName().getQualifier()); |
|
} |
|
|
|
public final boolean inSamePackage(ClassDefinition c) { |
|
// find out if the class stored in c is defined in the same |
|
|
|
return inSamePackage(c.getName().getQualifier()); |
|
} |
|
|
|
public final boolean inSamePackage(Identifier packageName) { |
|
return (getName().getQualifier().equals(packageName)); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final boolean isInterface() { |
|
return (getModifiers() & M_INTERFACE) != 0; |
|
} |
|
public final boolean isClass() { |
|
return (getModifiers() & M_INTERFACE) == 0; |
|
} |
|
public final boolean isPublic() { |
|
return (getModifiers() & M_PUBLIC) != 0; |
|
} |
|
public final boolean isPrivate() { |
|
return (getModifiers() & M_PRIVATE) != 0; |
|
} |
|
public final boolean isProtected() { |
|
return (getModifiers() & M_PROTECTED) != 0; |
|
} |
|
public final boolean isPackagePrivate() { |
|
return (modifiers & (M_PUBLIC | M_PRIVATE | M_PROTECTED)) == 0; |
|
} |
|
public final boolean isFinal() { |
|
return (getModifiers() & M_FINAL) != 0; |
|
} |
|
public final boolean isAbstract() { |
|
return (getModifiers() & M_ABSTRACT) != 0; |
|
} |
|
public final boolean isSynthetic() { |
|
return (getModifiers() & M_SYNTHETIC) != 0; |
|
} |
|
public final boolean isDeprecated() { |
|
return (getModifiers() & M_DEPRECATED) != 0; |
|
} |
|
public final boolean isAnonymous() { |
|
return (getModifiers() & M_ANONYMOUS) != 0; |
|
} |
|
public final boolean isLocal() { |
|
return (getModifiers() & M_LOCAL) != 0; |
|
} |
|
public final boolean hasConstructor() { |
|
return getFirstMatch(idInit) != null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final boolean mustBeAbstract(Environment env) { |
|
// If it is declared abstract, return true. |
|
|
|
if (isAbstract()) { |
|
return true; |
|
} |
|
|
|
// Check to see if the class should have been declared to be |
|
// abstract. |
|
|
|
// We make sure that the inherited method collection has been |
|
|
|
collectInheritedMethods(env); |
|
|
|
// We check for any abstract methods inherited or declared |
|
|
|
Iterator methods = getMethods(); |
|
while (methods.hasNext()) { |
|
MemberDefinition method = (MemberDefinition) methods.next(); |
|
|
|
if (method.isAbstract()) { |
|
return true; |
|
} |
|
} |
|
|
|
// We check for hidden "permanently abstract" methods in |
|
|
|
return getPermanentlyAbstractMethods().hasNext(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean superClassOf(Environment env, ClassDeclaration otherClass) |
|
throws ClassNotFound { |
|
while (otherClass != null) { |
|
if (getClassDeclaration().equals(otherClass)) { |
|
return true; |
|
} |
|
otherClass = otherClass.getClassDefinition(env).getSuperClass(); |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean enclosingClassOf(ClassDefinition otherClass) { |
|
while ((otherClass = otherClass.getOuterClass()) != null) { |
|
if (this == otherClass) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean subClassOf(Environment env, ClassDeclaration otherClass) throws ClassNotFound { |
|
ClassDeclaration c = getClassDeclaration(); |
|
while (c != null) { |
|
if (c.equals(otherClass)) { |
|
return true; |
|
} |
|
c = c.getClassDefinition(env).getSuperClass(); |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean implementedBy(Environment env, ClassDeclaration c) throws ClassNotFound { |
|
for (; c != null ; c = c.getClassDefinition(env).getSuperClass()) { |
|
if (getClassDeclaration().equals(c)) { |
|
return true; |
|
} |
|
ClassDeclaration intf[] = c.getClassDefinition(env).getInterfaces(); |
|
for (int i = 0 ; i < intf.length ; i++) { |
|
if (implementedBy(env, intf[i])) { |
|
return true; |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean couldImplement(ClassDefinition intDef) { |
|
|
|
if (!doInheritanceChecks) { |
|
throw new CompilerError("couldImplement: no checks"); |
|
} |
|
|
|
|
|
if (!isInterface() || !intDef.isInterface()) { |
|
throw new CompilerError("couldImplement: not interface"); |
|
} |
|
|
|
// Make sure we are not called before we have collected our |
|
|
|
if (allMethods == null) { |
|
throw new CompilerError("couldImplement: called early"); |
|
} |
|
|
|
// Get the other classes' methods. getMethods() in |
|
// general can return methods which are not visible to the |
|
// current package. We need to make sure that these do not |
|
|
|
Iterator otherMethods = intDef.getMethods(); |
|
|
|
while (otherMethods.hasNext()) { |
|
|
|
MemberDefinition method = |
|
(MemberDefinition) otherMethods.next(); |
|
|
|
Identifier name = method.getName(); |
|
Type type = method.getType(); |
|
|
|
|
|
MemberDefinition myMethod = allMethods.lookupSig(name, type); |
|
|
|
//System.out.println("Comparing\n\t" + myMethod + |
|
// "\nand\n\t" + method); |
|
|
|
if (myMethod != null) { |
|
|
|
if (!myMethod.sameReturnType(method)) { |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean extendsCanAccess(Environment env, ClassDeclaration c) throws ClassNotFound { |
|
|
|
// Names in the 'extends' or 'implements' clause of an inner class |
|
|
|
if (outerClass != null) { |
|
return outerClass.canAccess(env, c); |
|
} |
|
|
|
// We are a package member. |
|
|
|
ClassDefinition cdef = c.getClassDefinition(env); |
|
|
|
if (cdef.isLocal()) { |
|
// No locals should be in scope in the 'extends' or |
|
|
|
throw new CompilerError("top local"); |
|
} |
|
|
|
if (cdef.isInnerClass()) { |
|
MemberDefinition f = cdef.getInnerClassMember(); |
|
|
|
|
|
if (f.isPublic()) { |
|
return true; |
|
} |
|
|
|
// Private access is ok only from the same class nest. This can |
|
// happen only if the class represented by 'this' encloses the inner |
|
|
|
if (f.isPrivate()) { |
|
return getClassDeclaration().equals(f.getTopClass().getClassDeclaration()); |
|
} |
|
|
|
|
|
return getName().getQualifier().equals(f.getClassDeclaration().getName().getQualifier()); |
|
} |
|
|
|
|
|
if (cdef.isPublic()) { |
|
return true; |
|
} |
|
|
|
|
|
return getName().getQualifier().equals(c.getName().getQualifier()); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean canAccess(Environment env, ClassDeclaration c) throws ClassNotFound { |
|
ClassDefinition cdef = c.getClassDefinition(env); |
|
|
|
if (cdef.isLocal()) { |
|
|
|
return true; |
|
} |
|
|
|
if (cdef.isInnerClass()) { |
|
return canAccess(env, cdef.getInnerClassMember()); |
|
} |
|
|
|
|
|
if (cdef.isPublic()) { |
|
return true; |
|
} |
|
|
|
|
|
return getName().getQualifier().equals(c.getName().getQualifier()); |
|
} |
|
|
|
/** |
|
* Check if a field can be accessed from a class |
|
*/ |
|
|
|
public boolean canAccess(Environment env, MemberDefinition f) |
|
throws ClassNotFound { |
|
|
|
|
|
if (f.isPublic()) { |
|
return true; |
|
} |
|
|
|
if (f.isProtected() && subClassOf(env, f.getClassDeclaration())) { |
|
return true; |
|
} |
|
|
|
if (f.isPrivate()) { |
|
return getTopClass().getClassDeclaration() |
|
.equals(f.getTopClass().getClassDeclaration()); |
|
} |
|
|
|
return getName().getQualifier().equals(f.getClassDeclaration().getName().getQualifier()); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean permitInlinedAccess(Environment env, ClassDeclaration c) |
|
throws ClassNotFound { |
|
|
|
return (env.opt() && c.equals(declaration)) || |
|
(env.opt_interclass() && canAccess(env, c)); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean permitInlinedAccess(Environment env, MemberDefinition f) |
|
throws ClassNotFound { |
|
return (env.opt() |
|
&& (f.clazz.getClassDeclaration().equals(declaration))) || |
|
(env.opt_interclass() && canAccess(env, f)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean protectedAccess(Environment env, MemberDefinition f, |
|
Type accessorType) |
|
throws ClassNotFound |
|
{ |
|
|
|
return |
|
|
|
f.isStatic() |
|
|| |
|
(accessorType.isType(TC_ARRAY) && (f.getName() == idClone) |
|
&& (f.getType().getArgumentTypes().length == 0)) |
|
|| |
|
(accessorType.isType(TC_CLASS) |
|
&& env.getClassDefinition(accessorType.getClassName()) |
|
.subClassOf(env, getClassDeclaration())) |
|
|| |
|
(getName().getQualifier() |
|
.equals(f.getClassDeclaration().getName().getQualifier())); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition getAccessMember(Environment env, Context ctx, |
|
MemberDefinition field, boolean isSuper) { |
|
throw new CompilerError("binary getAccessMember"); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition getUpdateMember(Environment env, Context ctx, |
|
MemberDefinition field, boolean isSuper) { |
|
throw new CompilerError("binary getUpdateMember"); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition getVariable(Environment env, |
|
Identifier nm, |
|
ClassDefinition source) |
|
throws AmbiguousMember, ClassNotFound { |
|
|
|
return getVariable0(env, nm, source, true, true); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private MemberDefinition getVariable0(Environment env, |
|
Identifier nm, |
|
ClassDefinition source, |
|
boolean showPrivate, |
|
boolean showPackage) |
|
throws AmbiguousMember, ClassNotFound { |
|
|
|
|
|
for (MemberDefinition member = getFirstMatch(nm); |
|
member != null; |
|
member = member.getNextMatch()) { |
|
if (member.isVariable()) { |
|
if ((showPrivate || !member.isPrivate()) && |
|
(showPackage || !member.isPackagePrivate())) { |
|
|
|
return member; |
|
} else { |
|
// Even though this definition is not inherited, |
|
|
|
return null; |
|
} |
|
} |
|
} |
|
|
|
|
|
ClassDeclaration sup = getSuperClass(); |
|
MemberDefinition field = null; |
|
if (sup != null) { |
|
field = |
|
sup.getClassDefinition(env) |
|
.getVariable0(env, nm, source, |
|
false, |
|
showPackage && inSamePackage(sup)); |
|
} |
|
|
|
|
|
for (int i = 0 ; i < interfaces.length ; i++) { |
|
// Try to look up the field in an interface. Since interfaces |
|
// only have public fields, the values of the two boolean |
|
|
|
MemberDefinition field2 = |
|
interfaces[i].getClassDefinition(env) |
|
.getVariable0(env, nm, source, true, true); |
|
|
|
if (field2 != null) { |
|
// If we have two different, accessible fields, then |
|
|
|
if (field != null && |
|
source.canAccess(env, field) && |
|
field2 != field) { |
|
|
|
throw new AmbiguousMember(field2, field); |
|
} |
|
field = field2; |
|
} |
|
} |
|
return field; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public boolean reportDeprecated(Environment env) { |
|
return (isDeprecated() |
|
|| (outerClass != null && outerClass.reportDeprecated(env))); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public void noteUsedBy(ClassDefinition ref, long where, Environment env) { |
|
|
|
if (reportDeprecated(env)) { |
|
env.error(where, "warn.class.is.deprecated", this); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition getInnerClass(Environment env, Identifier nm) |
|
throws ClassNotFound { |
|
// Note: AmbiguousClass will not be thrown unless and until |
|
// inner classes can be defined inside interfaces. |
|
|
|
|
|
for (MemberDefinition field = getFirstMatch(nm); |
|
field != null ; field = field.getNextMatch()) { |
|
if (field.isInnerClass()) { |
|
if (field.getInnerClass().isLocal()) { |
|
continue; |
|
} |
|
return field; |
|
} |
|
} |
|
|
|
// Get it from the super class |
|
// It is likely that 'getSuperClass()' could be made to work here |
|
// but we would have to assure somehow that 'resolveTypeStructure' |
|
// has been called on the current class nest. Since we can get |
|
// here from 'resolveName', which is called from 'resolveSupers', |
|
// it is possible that the first attempt to resolve the superclass |
|
// will originate here, instead of in the call to 'getSuperClass' |
|
// in 'checkSupers'. See 'resolveTypeStructure', in which a call |
|
// to 'resolveSupers' precedes the call to 'checkSupers'. Why is |
|
// name resolution done twice, first in 'resolveName'? |
|
// NOTE: 'SourceMember.resolveTypeStructure' may initiate type |
|
// structure resolution for an inner class. Normally, this |
|
// occurs during the resolution of the outer class, but fields |
|
// added after the resolution of their containing class will |
|
// be resolved late -- see 'addMember(env,field)' below. |
|
// This should only happen for synthetic members, which should |
|
|
|
ClassDeclaration sup = getSuperClass(env); |
|
if (sup != null) |
|
return sup.getClassDefinition(env).getInnerClass(env, nm); |
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private MemberDefinition matchMethod(Environment env, |
|
ClassDefinition accessor, |
|
Identifier methodName, |
|
Type[] argumentTypes, |
|
boolean isAnonConstCall, |
|
Identifier accessPackage) |
|
throws AmbiguousMember, ClassNotFound { |
|
|
|
if (allMethods == null || !allMethods.isFrozen()) { |
|
|
|
throw new CompilerError("matchMethod called early"); |
|
// collectInheritedMethods(env); |
|
} |
|
|
|
|
|
MemberDefinition tentative = null; |
|
|
|
|
|
List candidateList = null; |
|
|
|
// Get all the methods inherited by this class which |
|
|
|
Iterator methods = allMethods.lookupName(methodName); |
|
|
|
while (methods.hasNext()) { |
|
MemberDefinition method = (MemberDefinition)methods.next(); |
|
|
|
|
|
if (!env.isApplicable(method, argumentTypes)) { |
|
continue; |
|
} |
|
|
|
|
|
if (accessor != null) { |
|
if (!accessor.canAccess(env, method)) { |
|
continue; |
|
} |
|
} else if (isAnonConstCall) { |
|
if (method.isPrivate() || |
|
(method.isPackagePrivate() && |
|
accessPackage != null && |
|
!inSamePackage(accessPackage))) { |
|
// For anonymous constructor accesses, we |
|
// haven't yet built an accessing class. |
|
// We disallow anonymous classes from seeing |
|
// private/package-private inaccessible |
|
|
|
continue; |
|
} |
|
} else { |
|
// If accessor is null, we assume that the access |
|
// is allowed. Query: is this option used? |
|
} |
|
|
|
if (tentative == null) { |
|
|
|
tentative = method; |
|
} else { |
|
if (env.isMoreSpecific(method, tentative)) { |
|
// We have found a method which is a strictly better |
|
|
|
tentative = method; |
|
} else { |
|
// If this method could possibly be another |
|
// maximally specific method, add it to our |
|
|
|
if (!env.isMoreSpecific(tentative,method)) { |
|
if (candidateList == null) { |
|
candidateList = new ArrayList(); |
|
} |
|
candidateList.add(method); |
|
} |
|
} |
|
} |
|
} |
|
|
|
if (tentative != null && candidateList != null) { |
|
// Find out if our `tentative' match is a uniquely |
|
|
|
Iterator candidates = candidateList.iterator(); |
|
while (candidates.hasNext()) { |
|
MemberDefinition method = (MemberDefinition)candidates.next(); |
|
if (!env.isMoreSpecific(tentative, method)) { |
|
throw new AmbiguousMember(tentative, method); |
|
} |
|
} |
|
} |
|
|
|
return tentative; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition matchMethod(Environment env, |
|
ClassDefinition accessor, |
|
Identifier methodName, |
|
Type[] argumentTypes) |
|
throws AmbiguousMember, ClassNotFound { |
|
|
|
return matchMethod(env, accessor, methodName, |
|
argumentTypes, false, null); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition matchMethod(Environment env, |
|
ClassDefinition accessor, |
|
Identifier methodName) |
|
throws AmbiguousMember, ClassNotFound { |
|
|
|
return matchMethod(env, accessor, methodName, |
|
Type.noArgs, false, null); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition matchAnonConstructor(Environment env, |
|
Identifier accessPackage, |
|
Type argumentTypes[]) |
|
throws AmbiguousMember, ClassNotFound { |
|
|
|
return matchMethod(env, null, idInit, argumentTypes, |
|
true, accessPackage); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition findMethod(Environment env, Identifier nm, Type t) |
|
throws ClassNotFound { |
|
|
|
MemberDefinition f; |
|
for (f = getFirstMatch(nm) ; f != null ; f = f.getNextMatch()) { |
|
|
|
if (f.getType().equalArguments(t)) { |
|
return f; |
|
} |
|
} |
|
|
|
|
|
if (nm.equals(idInit)) { |
|
return null; |
|
} |
|
|
|
|
|
ClassDeclaration sup = getSuperClass(); |
|
if (sup == null) |
|
return null; |
|
|
|
return sup.getClassDefinition(env).findMethod(env, nm, t); |
|
} |
|
|
|
|
|
protected void basicCheck(Environment env) throws ClassNotFound { |
|
|
|
if (outerClass != null) |
|
outerClass.basicCheck(env); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void check(Environment env) throws ClassNotFound { |
|
} |
|
|
|
public Vset checkLocalClass(Environment env, Context ctx, |
|
Vset vset, ClassDefinition sup, |
|
Expression args[], Type argTypes[] |
|
) throws ClassNotFound { |
|
throw new CompilerError("checkLocalClass"); |
|
} |
|
|
|
//--------------------------------------------------------------- |
|
// The non-synthetic methods defined in this class or in any |
|
// of its parents (class or interface). This member is used |
|
// to cache work done in collectInheritedMethods for use by |
|
// getMethods() and matchMethod(). It should be accessed by |
|
|
|
MethodSet allMethods = null; |
|
|
|
// One of our superclasses may contain an abstract method which |
|
// we are unable to ever implement. This happens when there is |
|
// a package-private abstract method in our parent and we are in |
|
// a different package than our parent. In these cases, we |
|
// keep a list of the "permanently abstract" or "unimplementable" |
|
// methods so that we can correctly detect that this class is |
|
// indeed abstract and so that we can give somewhat comprehensible |
|
|
|
private List permanentlyAbstractMethods = new ArrayList(); |
|
|
|
|
|
|
|
|
|
*/ |
|
protected Iterator getPermanentlyAbstractMethods() { |
|
|
|
if (allMethods == null) { |
|
throw new CompilerError("isPermanentlyAbstract() called early"); |
|
} |
|
|
|
return permanentlyAbstractMethods.iterator(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
protected static boolean doInheritanceChecks = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static void turnOffInheritanceChecks() { |
|
doInheritanceChecks = false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void collectOneClass(Environment env, |
|
ClassDeclaration parent, |
|
MethodSet myMethods, |
|
MethodSet allMethods, |
|
MethodSet mirandaMethods) { |
|
|
|
// System.out.println("Inheriting methods from " + parent); |
|
|
|
try { |
|
ClassDefinition pClass = parent.getClassDefinition(env); |
|
Iterator methods = pClass.getMethods(env); |
|
while (methods.hasNext()) { |
|
MemberDefinition method = |
|
(MemberDefinition) methods.next(); |
|
|
|
// Private methods are not inherited. |
|
// |
|
// Constructors are not inherited. |
|
// |
|
// Any non-abstract methods in an interface come |
|
// from java.lang.Object. This means that they |
|
// should have already been added to allMethods |
|
|
|
if (method.isPrivate() || |
|
method.isConstructor() || |
|
(pClass.isInterface() && !method.isAbstract())) { |
|
|
|
continue; |
|
} |
|
|
|
|
|
Identifier name = method.getName(); |
|
Type type = method.getType(); |
|
|
|
// Check for a method of the same signature which |
|
|
|
MemberDefinition override = |
|
myMethods.lookupSig(name, type); |
|
|
|
// Is this method inaccessible due to package-private |
|
|
|
if (method.isPackagePrivate() && |
|
!inSamePackage(method.getClassDeclaration())) { |
|
|
|
if (override != null && this instanceof |
|
sun.tools.javac.SourceClass) { |
|
// We give a warning when a class shadows an |
|
// inaccessible package-private method from |
|
// its superclass. This warning is meant |
|
// to prevent people from relying on overriding |
|
// when it does not happen. This warning should |
|
// probably be removed to be consistent with the |
|
// general "no warnings" policy of this |
|
// compiler. |
|
// |
|
// The `instanceof' above is a hack so that only |
|
// SourceClass generates this warning, not a |
|
|
|
env.error(method.getWhere(), |
|
"warn.no.override.access", |
|
override, |
|
override.getClassDeclaration(), |
|
method.getClassDeclaration()); |
|
} |
|
|
|
// If our superclass has a package-private abstract |
|
// method that we have no access to, then we add |
|
// this method to our list of permanently abstract |
|
// methods. The idea is, since we cannot override |
|
// the method, we can never make this class |
|
|
|
if (method.isAbstract()) { |
|
permanentlyAbstractMethods.add(method); |
|
} |
|
|
|
|
|
continue; |
|
} |
|
|
|
if (override != null) { |
|
// `method' and `override' have the same signature. |
|
// We are required to check that `override' is a |
|
// legal override of `method' |
|
|
|
//System.out.println ("About to check override of " + |
|
// method); |
|
|
|
override.checkOverride(env, method); |
|
} else { |
|
// In the absence of a definition in the class |
|
// itself, we check to see if this definition |
|
// can be successfully merged with any other |
|
// inherited definitions. |
|
|
|
// Have we added a member of the same signature |
|
|
|
MemberDefinition formerMethod = |
|
allMethods.lookupSig(name, type); |
|
|
|
// If the previous definition is nonexistent or |
|
|
|
if (formerMethod == null) { |
|
//System.out.println("Added " + method + " to " + |
|
// this); |
|
|
|
if (mirandaMethods != null && |
|
pClass.isInterface() && !isInterface()) { |
|
// Whenever a class inherits a method |
|
// from an interface, that method is |
|
// one of our "miranda" methods. Early |
|
// VMs require that these methods be |
|
// added as true members to the class |
|
// to enable method lookup to work in the |
|
|
|
method = |
|
new sun.tools.javac.SourceMember(method,this, |
|
env); |
|
mirandaMethods.add(method); |
|
|
|
//System.out.println("Added " + method + |
|
// " to " + this + " as a Miranda"); |
|
} |
|
|
|
// There is no previous inherited definition. |
|
|
|
allMethods.add(method); |
|
} else if (isInterface() && |
|
!formerMethod.isAbstract() && |
|
method.isAbstract()) { |
|
// If we are in an interface and we have inherited |
|
// both an abstract method and a non-abstract method |
|
// then we know that the non-abstract method is |
|
// a placeholder from Object put in for type checking |
|
// and the abstract method was already checked to |
|
|
|
allMethods.replace(method); |
|
|
|
} else { |
|
// Okay, `formerMethod' and `method' both have the |
|
// same signature. See if they are compatible. |
|
|
|
//System.out.println ("About to check meet of " + |
|
// method); |
|
|
|
if (!formerMethod.checkMeet(env, |
|
method, |
|
this.getClassDeclaration())) { |
|
// The methods are incompatible. Skip to |
|
|
|
continue; |
|
} |
|
|
|
if (formerMethod.couldOverride(env, method)) { |
|
// Do nothing. The current definition |
|
// is specific enough. |
|
|
|
//System.out.println("trivial meet of " + |
|
|
|
continue; |
|
} |
|
|
|
if (method.couldOverride(env, formerMethod)) { |
|
// `method' is more specific than |
|
// `formerMethod'. replace `formerMethod'. |
|
|
|
|
|
if (mirandaMethods != null && |
|
pClass.isInterface() && !isInterface()) { |
|
// Whenever a class inherits a method |
|
// from an interface, that method is |
|
// one of our "miranda" methods. Early |
|
// VMs require that these methods be |
|
// added as true members to the class |
|
// to enable method lookup to work in the |
|
|
|
method = |
|
new sun.tools.javac.SourceMember(method, |
|
this,env); |
|
|
|
mirandaMethods.replace(method); |
|
|
|
//System.out.println("Added " + method + |
|
// " to " + this + " as a Miranda"); |
|
} |
|
|
|
allMethods.replace(method); |
|
|
|
continue; |
|
} |
|
|
|
// Neither method is more specific than the other. |
|
// Oh well. We need to construct a nontrivial |
|
// meet of the two methods. |
|
// |
|
// This is not yet implemented, so we give |
|
|
|
env.error(this.where, |
|
"nontrivial.meet", method, |
|
formerMethod.getClassDefinition(), |
|
method.getClassDeclaration() |
|
); |
|
} |
|
} |
|
} |
|
} catch (ClassNotFound ee) { |
|
env.error(getWhere(), "class.not.found", ee.name, this); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void collectInheritedMethods(Environment env) { |
|
|
|
MethodSet myMethods; |
|
MethodSet mirandaMethods; |
|
|
|
//System.out.println("Called collectInheritedMethods() for " + |
|
// this); |
|
|
|
if (allMethods != null) { |
|
if (allMethods.isFrozen()) { |
|
// We have already done the collection. No need to |
|
|
|
return; |
|
} else { |
|
// We have run into a circular need to collect our methods. |
|
|
|
throw new CompilerError("collectInheritedMethods()"); |
|
} |
|
} |
|
|
|
myMethods = new MethodSet(); |
|
allMethods = new MethodSet(); |
|
|
|
|
|
if (env.version12()) { |
|
mirandaMethods = null; |
|
} else { |
|
mirandaMethods = new MethodSet(); |
|
} |
|
|
|
// Any methods defined in the current class get added |
|
// to both the myMethods and the allMethods MethodSets. |
|
|
|
for (MemberDefinition member = getFirstMember(); |
|
member != null; |
|
member = member.nextMember) { |
|
|
|
|
|
if (member.isMethod() && |
|
!member.isInitializer()) { |
|
|
|
//System.out.println("Declared in " + this + ", " + member); |
|
|
|
//////////////////////////////////////////////////////////// |
|
// PCJ 2003-07-30 modified the following code because with |
|
// the covariant return type feature of the 1.5 compiler, |
|
// there might be multiple methods with the same signature |
|
// but different return types, and MethodSet doesn't |
|
// support that. We use a new utility method that attempts |
|
// to ensure that the appropriate method winds up in the |
|
// MethodSet. See 4892308. |
|
//////////////////////////////////////////////////////////// |
|
// myMethods.add(member); |
|
// allMethods.add(member); |
|
|
|
methodSetAdd(env, myMethods, member); |
|
methodSetAdd(env, allMethods, member); |
|
//////////////////////////////////////////////////////////// |
|
} |
|
} |
|
|
|
// We're ready to start adding inherited methods. First add |
|
// the methods from our superclass. |
|
|
|
//System.out.println("About to start superclasses for " + this); |
|
|
|
ClassDeclaration scDecl = getSuperClass(env); |
|
if (scDecl != null) { |
|
collectOneClass(env, scDecl, |
|
myMethods, allMethods, mirandaMethods); |
|
|
|
// Make sure that we add all unimplementable methods from our |
|
|
|
ClassDefinition sc = scDecl.getClassDefinition(); |
|
Iterator supIter = sc.getPermanentlyAbstractMethods(); |
|
while (supIter.hasNext()) { |
|
permanentlyAbstractMethods.add(supIter.next()); |
|
} |
|
} |
|
|
|
// Now we inherit all of the methods from our interfaces. |
|
|
|
//System.out.println("About to start interfaces for " + this); |
|
|
|
for (int i = 0; i < interfaces.length; i++) { |
|
collectOneClass(env, interfaces[i], |
|
myMethods, allMethods, mirandaMethods); |
|
} |
|
allMethods.freeze(); |
|
|
|
// Now we have collected all of our methods from our superclasses |
|
// and interfaces into our `allMethods' member. Good. As a last |
|
// task, we add our collected miranda methods to this class. |
|
// |
|
// If we do not add the mirandas to the class explicitly, there |
|
|
|
if (mirandaMethods != null && mirandaMethods.size() > 0) { |
|
addMirandaMethods(env, mirandaMethods.iterator()); |
|
} |
|
} |
|
|
|
//////////////////////////////////////////////////////////// |
|
// PCJ 2003-07-30 added this utility method to insulate |
|
// MethodSet additions from the covariant return type |
|
// feature of the 1.5 compiler. When there are multiple |
|
// methods with the same signature and different return |
|
// types to be added, we try to ensure that the one with |
|
// the most specific return type winds up in the MethodSet. |
|
// This logic was not put into MethodSet itself because it |
|
// requires access to an Environment for type relationship |
|
// checking. No error checking is performed here, but that |
|
// should be OK because this code is only still used by |
|
// rmic. See 4892308. |
|
|
|
private static void methodSetAdd(Environment env, |
|
MethodSet methodSet, |
|
MemberDefinition newMethod) |
|
{ |
|
MemberDefinition oldMethod = methodSet.lookupSig(newMethod.getName(), |
|
newMethod.getType()); |
|
if (oldMethod != null) { |
|
Type oldReturnType = oldMethod.getType().getReturnType(); |
|
Type newReturnType = newMethod.getType().getReturnType(); |
|
try { |
|
if (env.isMoreSpecific(newReturnType, oldReturnType)) { |
|
methodSet.replace(newMethod); |
|
} |
|
} catch (ClassNotFound ignore) { |
|
} |
|
} else { |
|
methodSet.add(newMethod); |
|
} |
|
} |
|
//////////////////////////////////////////////////////////// |
|
|
|
|
|
|
|
|
|
*/ |
|
public Iterator getMethods(Environment env) { |
|
if (allMethods == null) { |
|
collectInheritedMethods(env); |
|
} |
|
return getMethods(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Iterator getMethods() { |
|
if (allMethods == null) { |
|
throw new CompilerError("getMethods: too early"); |
|
} |
|
return allMethods.iterator(); |
|
} |
|
|
|
// In early VM's there was a bug -- the VM didn't walk the interfaces |
|
// of a class looking for a method, they only walked the superclass |
|
// chain. This meant that abstract methods defined only in interfaces |
|
// were not being found. To fix this bug, a counter-bug was introduced |
|
// in the compiler -- the so-called Miranda methods. If a class |
|
// does not provide a definition for an abstract method in one of |
|
// its interfaces then the compiler inserts one in the class artificially. |
|
// That way the VM didn't have to bother looking at the interfaces. |
|
// |
|
// This is a problem. Miranda methods are not part of the specification. |
|
// But they continue to be inserted so that old VM's can run new code. |
|
// Someday, when the old VM's are gone, perhaps classes can be compiled |
|
// without Miranda methods. Towards this end, the compiler has a |
|
// flag, -nomiranda, which can turn off the creation of these methods. |
|
// Eventually that behavior should become the default. |
|
// |
|
// Why are they called Miranda methods? Well the sentence "If the |
|
// class is not able to provide a method, then one will be provided |
|
// by the compiler" is very similar to the sentence "If you cannot |
|
// afford an attorney, one will be provided by the court," -- one |
|
// of the so-called "Miranda" rights in the United States. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
protected void addMirandaMethods(Environment env, |
|
Iterator mirandas) { |
|
// do nothing. |
|
} |
|
|
|
//--------------------------------------------------------------- |
|
|
|
public void inlineLocalClass(Environment env) { |
|
} |
|
|
|
/** |
|
* We create a stub for this. Source classes do more work. |
|
* Some calls from 'SourceClass.checkSupers' execute this method. |
|
* @see sun.tools.javac.SourceClass#resolveTypeStructure |
|
*/ |
|
|
|
public void resolveTypeStructure(Environment env) { |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Identifier resolveName(Environment env, Identifier name) { |
|
if (tracing) env.dtEvent("ClassDefinition.resolveName: " + name); |
|
// This logic is pretty much exactly parallel to that of |
|
|
|
if (name.isQualified()) { |
|
// Try to resolve the first identifier component, |
|
// because inner class names take precedence over |
|
|
|
Identifier rhead = resolveName(env, name.getHead()); |
|
|
|
if (rhead.hasAmbigPrefix()) { |
|
// The first identifier component refers to an |
|
// ambiguous class. Limp on. We throw away the |
|
// rest of the classname as it is irrelevant. |
|
|
|
return rhead; |
|
} |
|
|
|
if (!env.classExists(rhead)) { |
|
return env.resolvePackageQualifiedName(name); |
|
} |
|
try { |
|
return env.getClassDefinition(rhead). |
|
resolveInnerClass(env, name.getTail()); |
|
} catch (ClassNotFound ee) { |
|
|
|
return Identifier.lookupInner(rhead, name.getTail()); |
|
} |
|
} |
|
|
|
// This method used to fail to look for local classes, thus a |
|
// reference to a local class within, e.g., the type of a member |
|
// declaration, would fail to resolve if the immediately enclosing |
|
// context was an inner class. The code added below is ugly, but |
|
// it works, and is lifted from existing code in 'Context.resolveName' |
|
// and 'Context.getClassCommon'. See the comments there about the design. |
|
// Fixes 4095716. |
|
|
|
int ls = -2; |
|
LocalMember lf = null; |
|
if (classContext != null) { |
|
lf = classContext.getLocalClass(name); |
|
if (lf != null) { |
|
ls = lf.getScopeNumber(); |
|
} |
|
} |
|
|
|
|
|
for (ClassDefinition c = this; c != null; c = c.outerClass) { |
|
try { |
|
MemberDefinition f = c.getInnerClass(env, name); |
|
if (f != null && |
|
(lf == null || classContext.getScopeNumber(c) > ls)) { |
|
// An uplevel member was found, and was nested more deeply than |
|
|
|
return f.getInnerClass().getName(); |
|
} |
|
} catch (ClassNotFound ee) { |
|
// a missing superclass, or something catastrophic |
|
} |
|
} |
|
|
|
|
|
if (lf != null) { |
|
return lf.getInnerClass().getName(); |
|
} |
|
|
|
|
|
return env.resolveName(name); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Identifier resolveInnerClass(Environment env, Identifier nm) { |
|
if (nm.isInner()) throw new CompilerError("inner"); |
|
if (nm.isQualified()) { |
|
Identifier rhead = resolveInnerClass(env, nm.getHead()); |
|
try { |
|
return env.getClassDefinition(rhead). |
|
resolveInnerClass(env, nm.getTail()); |
|
} catch (ClassNotFound ee) { |
|
|
|
return Identifier.lookupInner(rhead, nm.getTail()); |
|
} |
|
} else { |
|
try { |
|
MemberDefinition f = getInnerClass(env, nm); |
|
if (f != null) { |
|
return f.getInnerClass().getName(); |
|
} |
|
} catch (ClassNotFound ee) { |
|
// a missing superclass, or something catastrophic |
|
} |
|
|
|
return Identifier.lookupInner(this.getName(), nm); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean innerClassExists(Identifier nm) { |
|
for (MemberDefinition field = getFirstMatch(nm.getHead()) ; field != null ; field = field.getNextMatch()) { |
|
if (field.isInnerClass()) { |
|
if (field.getInnerClass().isLocal()) { |
|
continue; |
|
} |
|
return !nm.isQualified() || |
|
field.getInnerClass().innerClassExists(nm.getTail()); |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition findAnyMethod(Environment env, Identifier nm) throws ClassNotFound { |
|
MemberDefinition f; |
|
for (f = getFirstMatch(nm) ; f != null ; f = f.getNextMatch()) { |
|
if (f.isMethod()) { |
|
return f; |
|
} |
|
} |
|
|
|
|
|
ClassDeclaration sup = getSuperClass(); |
|
if (sup == null) |
|
return null; |
|
return sup.getClassDefinition(env).findAnyMethod(env, nm); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int diagnoseMismatch(Environment env, Identifier nm, Type argTypes[], |
|
int start, Type margTypeResult[]) throws ClassNotFound { |
|
int haveMatch[] = new int[argTypes.length]; |
|
Type margType[] = new Type[argTypes.length]; |
|
if (!diagnoseMismatch(env, nm, argTypes, start, haveMatch, margType)) |
|
return -2; |
|
for (int i = start; i < argTypes.length; i++) { |
|
if (haveMatch[i] < 4) { |
|
margTypeResult[0] = margType[i]; |
|
return (i<<2) | haveMatch[i]; |
|
} |
|
} |
|
return -1; |
|
} |
|
|
|
private boolean diagnoseMismatch(Environment env, Identifier nm, Type argTypes[], int start, |
|
int haveMatch[], Type margType[]) throws ClassNotFound { |
|
|
|
boolean haveOne = false; |
|
MemberDefinition f; |
|
for (f = getFirstMatch(nm) ; f != null ; f = f.getNextMatch()) { |
|
if (!f.isMethod()) { |
|
continue; |
|
} |
|
Type fArgTypes[] = f.getType().getArgumentTypes(); |
|
if (fArgTypes.length == argTypes.length) { |
|
haveOne = true; |
|
for (int i = start; i < argTypes.length; i++) { |
|
Type at = argTypes[i]; |
|
Type ft = fArgTypes[i]; |
|
if (env.implicitCast(at, ft)) { |
|
haveMatch[i] = 4; |
|
continue; |
|
} else if (haveMatch[i] <= 2 && env.explicitCast(at, ft)) { |
|
if (haveMatch[i] < 2) margType[i] = null; |
|
haveMatch[i] = 2; |
|
} else if (haveMatch[i] > 0) { |
|
continue; |
|
} |
|
if (margType[i] == null) |
|
margType[i] = ft; |
|
else if (margType[i] != ft) |
|
haveMatch[i] |= 1; |
|
} |
|
} |
|
} |
|
|
|
|
|
if (nm.equals(idInit)) { |
|
return haveOne; |
|
} |
|
|
|
|
|
ClassDeclaration sup = getSuperClass(); |
|
if (sup != null) { |
|
if (sup.getClassDefinition(env).diagnoseMismatch(env, nm, argTypes, start, |
|
haveMatch, margType)) |
|
haveOne = true; |
|
} |
|
return haveOne; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void addMember(MemberDefinition field) { |
|
|
|
if (firstMember == null) { |
|
firstMember = lastMember = field; |
|
} else if (field.isSynthetic() && field.isFinal() |
|
&& field.isVariable()) { |
|
|
|
field.nextMember = firstMember; |
|
firstMember = field; |
|
field.nextMatch = (MemberDefinition)fieldHash.get(field.name); |
|
} else { |
|
lastMember.nextMember = field; |
|
lastMember = field; |
|
field.nextMatch = (MemberDefinition)fieldHash.get(field.name); |
|
} |
|
fieldHash.put(field.name, field); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void addMember(Environment env, MemberDefinition field) { |
|
addMember(field); |
|
if (resolved) { |
|
|
|
field.resolveTypeStructure(env); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public UplevelReference getReference(LocalMember target) { |
|
for (UplevelReference r = references; r != null; r = r.getNext()) { |
|
if (r.getTarget() == target) { |
|
return r; |
|
} |
|
} |
|
return addReference(target); |
|
} |
|
|
|
protected UplevelReference addReference(LocalMember target) { |
|
if (target.getClassDefinition() == this) { |
|
throw new CompilerError("addReference "+target); |
|
} |
|
referencesMustNotBeFrozen(); |
|
UplevelReference r = new UplevelReference(this, target); |
|
references = r.insertInto(references); |
|
return r; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public UplevelReference getReferences() { |
|
return references; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public UplevelReference getReferencesFrozen() { |
|
referencesFrozen = true; |
|
return references; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public final void referencesMustNotBeFrozen() { |
|
if (referencesFrozen) { |
|
throw new CompilerError("referencesMustNotBeFrozen "+this); |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public MemberDefinition getClassLiteralLookup(long fwhere) { |
|
throw new CompilerError("binary class"); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public void addDependency(ClassDeclaration c) { |
|
throw new CompilerError("addDependency"); |
|
} |
|
|
|
/** |
|
* Maintain a hash table of local and anonymous classes |
|
* whose internal names are prefixed by the current class. |
|
* The key is the simple internal name, less the prefix. |
|
*/ |
|
|
|
public ClassDefinition getLocalClass(String name) { |
|
if (localClasses == null) { |
|
return null; |
|
} else { |
|
return (ClassDefinition)localClasses.get(name); |
|
} |
|
} |
|
|
|
public void addLocalClass(ClassDefinition c, String name) { |
|
if (localClasses == null) { |
|
localClasses = new Hashtable(LOCAL_CLASSES_SIZE); |
|
} |
|
localClasses.put(name, c); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public void print(PrintStream out) { |
|
if (isPublic()) { |
|
out.print("public "); |
|
} |
|
if (isInterface()) { |
|
out.print("interface "); |
|
} else { |
|
out.print("class "); |
|
} |
|
out.print(getName() + " "); |
|
if (getSuperClass() != null) { |
|
out.print("extends " + getSuperClass().getName() + " "); |
|
} |
|
if (interfaces.length > 0) { |
|
out.print("implements "); |
|
for (int i = 0 ; i < interfaces.length ; i++) { |
|
if (i > 0) { |
|
out.print(", "); |
|
} |
|
out.print(interfaces[i].getName()); |
|
out.print(" "); |
|
} |
|
} |
|
out.println("{"); |
|
|
|
for (MemberDefinition f = getFirstMember() ; f != null ; f = f.getNextMember()) { |
|
out.print(" "); |
|
f.print(out); |
|
} |
|
|
|
out.println("}"); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String toString() { |
|
return getClassDeclaration().toString(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public void cleanup(Environment env) { |
|
if (env.dump()) { |
|
env.output("[cleanup " + getName() + "]"); |
|
} |
|
for (MemberDefinition f = getFirstMember() ; f != null ; f = f.getNextMember()) { |
|
f.cleanup(env); |
|
} |
|
|
|
documentation = null; |
|
} |
|
} |