blob: 76e3b7b99fd5032536f1c23890c7fb001b41ad9c [file] [log] [blame]
/*
* Copyright (c) 1994, 2006, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
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;
/**
* This class is a Java class definition
*
* WARNING: The contents of this source file are not part of any
* supported API. Code that depends on them does so at its own risk:
* they are subject to change or removal without notice.
*/
public
class ClassDefinition implements Constants {
protected Object source;
protected long where;
protected int modifiers;
protected Identifier localName; // for local classes
protected ClassDeclaration declaration;
protected IdentifierToken superClassId;
protected IdentifierToken interfaceIds[];
protected ClassDeclaration superClass;
protected ClassDeclaration interfaces[];
protected ClassDefinition outerClass;
protected MemberDefinition outerMember;
protected MemberDefinition innerClassMember; // field for me in outerClass
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'
// and 'BatchEnvironment.makeClassDefinition'. Allocated on demand.
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
// of the fix for 4095716.
protected Context classContext;
// The saved class context is now also used in 'SourceClass.getAccessMember'.
// Provide read-only access via this method. Part of fix for 4098093.
public Context getClassContext() {
return classContext;
}
/**
* Constructor
*/
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;
}
/**
* Get the source of the class
*/
public final Object getSource() {
return source;
}
/**
* Check if there were any errors in this class.
*/
public final boolean getError() {
return error;
}
/**
* Mark this class to be erroneous.
*/
public final void setError() {
this.error = true;
setNestError();
}
/**
* Check if there were any errors in our class nest.
*/
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
// none of them.
return nestError || ((outerClass != null) ? outerClass.getNestError() : false);
}
/**
* Mark this class, and all siblings in its class nest, to be
* erroneous.
*/
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
// none of them.
outerClass.setNestError();
}
}
/**
* Get the position in the input
*/
public final long getWhere() {
return where;
}
/**
* Get the class declaration
*/
public final ClassDeclaration getClassDeclaration() {
return declaration;
}
/**
* Get the class' modifiers
*/
public final int getModifiers() {
return modifiers;
}
public final void subModifiers(int mod) {
modifiers &= ~mod;
}
public final void addModifiers(int mod) {
modifiers |= mod;
}
// *** DEBUG ***
protected boolean supersCheckStarted = !(this instanceof sun.tools.javac.SourceClass);
/**
* Get the class' super class
*/
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 ***
// This method should not be called if the superclass has not been resolved.
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();
}
/**
* Get the class' interfaces
*/
public final ClassDeclaration getInterfaces()[] {
if (interfaces == null) throw new CompilerError("getInterfaces");
return interfaces;
}
/**
* Get the class' enclosing class (or null if not inner)
*/
public final ClassDefinition getOuterClass() {
return outerClass;
}
/**
* Set the class' enclosing class. Must be done at most once.
*/
protected final void setOuterClass(ClassDefinition outerClass) {
if (this.outerClass != null) throw new CompilerError("setOuterClass");
this.outerClass = outerClass;
}
/**
* Set the class' enclosing current instance pointer.
* Must be done at most once.
*/
protected final void setOuterMember(MemberDefinition outerMember) {
if (isStatic() || !isInnerClass()) throw new CompilerError("setOuterField");
if (this.outerMember != null) throw new CompilerError("setOuterField");
this.outerMember = outerMember;
}
/**
* Tell if the class is inner.
* This predicate also returns true for top-level nested types.
* To test for a true inner class as seen by the programmer,
* use <tt>!isTopLevel()</tt>.
*/
public final boolean isInnerClass() {
return outerClass != null;
}
/**
* Tell if the class is a member of another class.
* This is false for package members and for block-local classes.
*/
public final boolean isMember() {
return outerClass != null && !isLocal();
}
/**
* Tell if the class is "top-level", which is either a package member,
* or a static member of another top-level class.
*/
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());
}
/**
* Tell if the class is local or or anonymous class, or inside
* such a class, which means it cannot be mentioned outside of
* its file.
*/
public final boolean isInsideLocalOrAnonymous() {
return isLocal() || isAnonymous () ||
(outerClass != null && outerClass.isInsideLocalOrAnonymous());
}
/**
* Return a simple identifier for this class (idNull if anonymous).
*/
public Identifier getLocalName() {
if (localName != null) {
return localName;
}
// This is also the name of the innerClassMember, if any:
return getName().getFlatName().getName();
}
/**
* Set the local name of a class. Must be a local class.
*/
public void setLocalName(Identifier name) {
if (isLocal()) {
localName = name;
}
}
/**
* If inner, get the field for this class in the enclosing class
*/
public final MemberDefinition getInnerClassMember() {
if (outerClass == null)
return null;
if (innerClassMember == null) {
// We must find the field in the outer class.
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;
}
/**
* If inner, return an innermost uplevel self pointer, if any exists.
* Otherwise, return null.
*/
public final MemberDefinition findOuterMember() {
return outerMember;
}
/**
* See if this is a (nested) static class.
*/
public final boolean isStatic() {
return (modifiers & ACC_STATIC) != 0;
}
/**
* Get the class' top-level enclosing class
*/
public final ClassDefinition getTopClass() {
ClassDefinition p, q;
for (p = this; (q = p.outerClass) != null; p = q)
;
return p;
}
/**
* Get the class' first field or first match
*/
public final MemberDefinition getFirstMember() {
return firstMember;
}
public final MemberDefinition getFirstMatch(Identifier name) {
return (MemberDefinition)fieldHash.get(name);
}
/**
* Get the class' name
*/
public final Identifier getName() {
return declaration.getName();
}
/**
* Get the class' type
*/
public final Type getType() {
return declaration.getType();
}
/**
* Get the class' documentation
*/
public String getDocumentation() {
return documentation;
}
/**
* Return true if the given documentation string contains a deprecation
* paragraph. This is true if the string contains the tag @deprecated
* is the first word in a line.
*/
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
// and the beginning of the line
for (int beg = scan-1; beg >= 0; beg--) {
char ch = documentation.charAt(beg);
if (ch == '\n' || ch == '\r') {
break; // OK
}
if (!Character.isSpace(ch)) {
continue doScan;
}
}
// make sure the char after the word is space or end of line
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
// package as the current class.
return inSamePackage(c.getName().getQualifier());
}
public final boolean inSamePackage(ClassDefinition c) {
// find out if the class stored in c is defined in the same
// package as the current class.
return inSamePackage(c.getName().getQualifier());
}
public final boolean inSamePackage(Identifier packageName) {
return (getName().getQualifier().equals(packageName));
}
/**
* Checks
*/
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;
}
/**
* Check to see if a class must be abstract. This method replaces
* isAbstract(env)
*/
public final boolean mustBeAbstract(Environment env) {
// If it is declared abstract, return true.
// (Fix for 4110534.)
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
// performed.
collectInheritedMethods(env);
// We check for any abstract methods inherited or declared
// by this class.
Iterator methods = getMethods();
while (methods.hasNext()) {
MemberDefinition method = (MemberDefinition) methods.next();
if (method.isAbstract()) {
return true;
}
}
// We check for hidden "permanently abstract" methods in
// our superclasses.
return getPermanentlyAbstractMethods().hasNext();
}
/**
* Check if this is a super class of another class
*/
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;
}
/**
* Check if this is an enclosing class of another class
*/
public boolean enclosingClassOf(ClassDefinition otherClass) {
while ((otherClass = otherClass.getOuterClass()) != null) {
if (this == otherClass) {
return true;
}
}
return false;
}
/**
* Check if this is a sub class of another class
*/
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;
}
/**
* Check if this class is implemented by another class
*/
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;
}
/**
* Check to see if a class which implements interface `this' could
* possibly implement the interface `intDef'. Note that the only
* way that this can fail is if `this' and `intDef' have methods
* which are of the same signature and different return types. This
* method is used by Environment.explicitCast() to determine if a
* cast between two interfaces is legal.
*
* This method should only be called on a class after it has been
* basicCheck()'ed.
*/
public boolean couldImplement(ClassDefinition intDef) {
// Check to see if we could have done the necessary checks.
if (!doInheritanceChecks) {
throw new CompilerError("couldImplement: no checks");
}
// This method should only be called for interfaces.
if (!isInterface() || !intDef.isInterface()) {
throw new CompilerError("couldImplement: not interface");
}
// Make sure we are not called before we have collected our
// inheritance information.
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
// prevent this class from being implemented.
Iterator otherMethods = intDef.getMethods();
while (otherMethods.hasNext()) {
// Get one of the methods from intDef...
MemberDefinition method =
(MemberDefinition) otherMethods.next();
Identifier name = method.getName();
Type type = method.getType();
// See if we implement a method of the same signature...
MemberDefinition myMethod = allMethods.lookupSig(name, type);
//System.out.println("Comparing\n\t" + myMethod +
// "\nand\n\t" + method);
if (myMethod != null) {
// We do. Make sure the methods have the same return type.
if (!myMethod.sameReturnType(method)) {
return false;
}
}
}
return true;
}
/**
* Check if another class can be accessed from the 'extends' or 'implements'
* clause of this class.
*/
public boolean extendsCanAccess(Environment env, ClassDeclaration c) throws ClassNotFound {
// Names in the 'extends' or 'implements' clause of an inner class
// are checked as if they appeared in the body of the surrounding 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
// 'implements' clause of a package member.
throw new CompilerError("top local");
}
if (cdef.isInnerClass()) {
MemberDefinition f = cdef.getInnerClassMember();
// Access to public member is always allowed.
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
// class represented by 'f'.
if (f.isPrivate()) {
return getClassDeclaration().equals(f.getTopClass().getClassDeclaration());
}
// Protected or default access -- allow access if in same package.
return getName().getQualifier().equals(f.getClassDeclaration().getName().getQualifier());
}
// Access to public member is always allowed.
if (cdef.isPublic()) {
return true;
}
// Default access -- allow access if in same package.
return getName().getQualifier().equals(c.getName().getQualifier());
}
/**
* Check if another class can be accessed from within the body of this class.
*/
public boolean canAccess(Environment env, ClassDeclaration c) throws ClassNotFound {
ClassDefinition cdef = c.getClassDefinition(env);
if (cdef.isLocal()) {
// if it's in scope, it's accessible
return true;
}
if (cdef.isInnerClass()) {
return canAccess(env, cdef.getInnerClassMember());
}
// Public access is always ok
if (cdef.isPublic()) {
return true;
}
// It must be in the same package
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 {
// Public access is always ok
if (f.isPublic()) {
return true;
}
// Protected access is ok from a subclass
if (f.isProtected() && subClassOf(env, f.getClassDeclaration())) {
return true;
}
// Private access is ok only from the same class nest
if (f.isPrivate()) {
return getTopClass().getClassDeclaration()
.equals(f.getTopClass().getClassDeclaration());
}
// It must be in the same package
return getName().getQualifier().equals(f.getClassDeclaration().getName().getQualifier());
}
/**
* Check if a class is entitled to inline access to a class from
* another class.
*/
public boolean permitInlinedAccess(Environment env, ClassDeclaration c)
throws ClassNotFound {
return (env.opt() && c.equals(declaration)) ||
(env.opt_interclass() && canAccess(env, c));
}
/**
* Check if a class is entitled to inline access to a method from
* another class.
*/
public boolean permitInlinedAccess(Environment env, MemberDefinition f)
throws ClassNotFound {
return (env.opt()
&& (f.clazz.getClassDeclaration().equals(declaration))) ||
(env.opt_interclass() && canAccess(env, f));
}
/**
* We know the the field is marked protected (and not public) and that
* the field is visible (as per canAccess). Can we access the field as
* <accessor>.<field>, where <accessor> has the type <accessorType>?
*
* Protected fields can only be accessed when the accessorType is a
* subclass of the current class
*/
public boolean protectedAccess(Environment env, MemberDefinition f,
Type accessorType)
throws ClassNotFound
{
return
// static protected fields are accessible
f.isStatic()
|| // allow array.clone()
(accessorType.isType(TC_ARRAY) && (f.getName() == idClone)
&& (f.getType().getArgumentTypes().length == 0))
|| // <accessorType> is a subtype of the current class
(accessorType.isType(TC_CLASS)
&& env.getClassDefinition(accessorType.getClassName())
.subClassOf(env, getClassDeclaration()))
|| // we are accessing the field from a friendly class (same package)
(getName().getQualifier()
.equals(f.getClassDeclaration().getName().getQualifier()));
}
/**
* Find or create an access method for a private member,
* or return null if this is not possible.
*/
public MemberDefinition getAccessMember(Environment env, Context ctx,
MemberDefinition field, boolean isSuper) {
throw new CompilerError("binary getAccessMember");
}
/**
* Find or create an update method for a private member,
* or return null if this is not possible.
*/
public MemberDefinition getUpdateMember(Environment env, Context ctx,
MemberDefinition field, boolean isSuper) {
throw new CompilerError("binary getUpdateMember");
}
/**
* Get a field from this class. Report ambiguous fields.
* If no accessible field is found, this method may return an
* inaccessible field to allow a useful error message.
*
* getVariable now takes the source class `source' as an argument.
* This allows getVariable to check whether a field is inaccessible
* before it signals that a field is ambiguous. The compiler used to
* signal an ambiguity even when one of the fields involved was not
* accessible. (bug 4053724)
*/
public MemberDefinition getVariable(Environment env,
Identifier nm,
ClassDefinition source)
throws AmbiguousMember, ClassNotFound {
return getVariable0(env, nm, source, true, true);
}
/*
* private fields are never inherited. package-private fields are
* not inherited across package boundaries. To capture this, we
* take two booleans as parameters: showPrivate indicates whether
* we have passed a class boundary, and showPackage indicates whether
* we have crossed a package boundary.
*/
private MemberDefinition getVariable0(Environment env,
Identifier nm,
ClassDefinition source,
boolean showPrivate,
boolean showPackage)
throws AmbiguousMember, ClassNotFound {
// Check to see if this field is defined in the current class
for (MemberDefinition member = getFirstMatch(nm);
member != null;
member = member.getNextMatch()) {
if (member.isVariable()) {
if ((showPrivate || !member.isPrivate()) &&
(showPackage || !member.isPackagePrivate())) {
// It is defined in this class.
return member;
} else {
// Even though this definition is not inherited,
// it hides all definitions in supertypes.
return null;
}
}
}
// Find the field in our superclass.
ClassDeclaration sup = getSuperClass();
MemberDefinition field = null;
if (sup != null) {
field =
sup.getClassDefinition(env)
.getVariable0(env, nm, source,
false,
showPackage && inSamePackage(sup));
}
// Find the field in our superinterfaces.
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
// arguments are not important.
MemberDefinition field2 =
interfaces[i].getClassDefinition(env)
.getVariable0(env, nm, source, true, true);
if (field2 != null) {
// If we have two different, accessible fields, then
// we've found an ambiguity.
if (field != null &&
source.canAccess(env, field) &&
field2 != field) {
throw new AmbiguousMember(field2, field);
}
field = field2;
}
}
return field;
}
/**
* Tells whether to report a deprecation error for this class.
*/
public boolean reportDeprecated(Environment env) {
return (isDeprecated()
|| (outerClass != null && outerClass.reportDeprecated(env)));
}
/**
* Note that this class is being used somehow by <tt>ref</tt>.
* Report deprecation errors, etc.
*/
public void noteUsedBy(ClassDefinition ref, long where, Environment env) {
// (Have this deal with canAccess() checks, too?)
if (reportDeprecated(env)) {
env.error(where, "warn.class.is.deprecated", this);
}
}
/**
* Get an inner class.
* Look in supers but not outers.
* (This is used directly to resolve expressions like "site.K", and
* inside a loop to resolve lone names like "K" or the "K" in "K.L".)
*
* Called from 'Context' and 'FieldExpression' as well as this class.
*
* @see FieldExpression.checkCommon
* @see resolveName
*/
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.
// Check if it is defined in the current class
for (MemberDefinition field = getFirstMatch(nm);
field != null ; field = field.getNextMatch()) {
if (field.isInnerClass()) {
if (field.getInnerClass().isLocal()) {
continue; // ignore this name; it is internally generated
}
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
// never be an inner class.
ClassDeclaration sup = getSuperClass(env);
if (sup != null)
return sup.getClassDefinition(env).getInnerClass(env, nm);
return null;
}
/**
* Lookup a method. This code implements the method lookup
* mechanism specified in JLS 15.11.2.
*
* This mechanism cannot be used to lookup synthetic methods.
*/
private MemberDefinition matchMethod(Environment env,
ClassDefinition accessor,
Identifier methodName,
Type[] argumentTypes,
boolean isAnonConstCall,
Identifier accessPackage)
throws AmbiguousMember, ClassNotFound {
if (allMethods == null || !allMethods.isFrozen()) {
// This may be too restrictive.
throw new CompilerError("matchMethod called early");
// collectInheritedMethods(env);
}
// A tentative maximally specific method.
MemberDefinition tentative = null;
// A list of other methods which may be maximally specific too.
List candidateList = null;
// Get all the methods inherited by this class which
// have the name `methodName'.
Iterator methods = allMethods.lookupName(methodName);
while (methods.hasNext()) {
MemberDefinition method = (MemberDefinition)methods.next();
// See if this method is applicable.
if (!env.isApplicable(method, argumentTypes)) {
continue;
}
// See if this method is accessible.
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
// constructors in their superclass.
continue;
}
} else {
// If accessor is null, we assume that the access
// is allowed. Query: is this option used?
}
if (tentative == null) {
// `method' becomes our tentative maximally specific match.
tentative = method;
} else {
if (env.isMoreSpecific(method, tentative)) {
// We have found a method which is a strictly better
// match than `tentative'. Replace it.
tentative = method;
} else {
// If this method could possibly be another
// maximally specific method, add it to our
// list of other candidates.
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
// maximally specific.
Iterator candidates = candidateList.iterator();
while (candidates.hasNext()) {
MemberDefinition method = (MemberDefinition)candidates.next();
if (!env.isMoreSpecific(tentative, method)) {
throw new AmbiguousMember(tentative, method);
}
}
}
return tentative;
}
/**
* Lookup a method. This code implements the method lookup
* mechanism specified in JLS 15.11.2.
*
* This mechanism cannot be used to lookup synthetic methods.
*/
public MemberDefinition matchMethod(Environment env,
ClassDefinition accessor,
Identifier methodName,
Type[] argumentTypes)
throws AmbiguousMember, ClassNotFound {
return matchMethod(env, accessor, methodName,
argumentTypes, false, null);
}
/**
* Lookup a method. This code implements the method lookup
* mechanism specified in JLS 15.11.2.
*
* This mechanism cannot be used to lookup synthetic methods.
*/
public MemberDefinition matchMethod(Environment env,
ClassDefinition accessor,
Identifier methodName)
throws AmbiguousMember, ClassNotFound {
return matchMethod(env, accessor, methodName,
Type.noArgs, false, null);
}
/**
* A version of matchMethod to be used only for constructors
* when we cannot pass in a sourceClass argument. We just assert
* our package name.
*
* This is used only for anonymous classes, where we have to look up
* a (potentially) protected constructor with no valid sourceClass
* parameter available.
*/
public MemberDefinition matchAnonConstructor(Environment env,
Identifier accessPackage,
Type argumentTypes[])
throws AmbiguousMember, ClassNotFound {
return matchMethod(env, null, idInit, argumentTypes,
true, accessPackage);
}
/**
* Find a method, ie: exact match in this class or any of the super
* classes.
*
* Only called by javadoc. For now I am holding off rewriting this
* code to rely on collectInheritedMethods(), as that code has
* not gotten along with javadoc in the past.
*/
public MemberDefinition findMethod(Environment env, Identifier nm, Type t)
throws ClassNotFound {
// look in the current class
MemberDefinition f;
for (f = getFirstMatch(nm) ; f != null ; f = f.getNextMatch()) {
// Note that non-method types return false for equalArguments().
if (f.getType().equalArguments(t)) {
return f;
}
}
// constructors are not inherited
if (nm.equals(idInit)) {
return null;
}
// look in the super class
ClassDeclaration sup = getSuperClass();
if (sup == null)
return null;
return sup.getClassDefinition(env).findMethod(env, nm, t);
}
// We create a stub for this. Source classes do more work.
protected void basicCheck(Environment env) throws ClassNotFound {
// Do the outer class first.
if (outerClass != null)
outerClass.basicCheck(env);
}
/**
* Check this class.
*/
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
// no other method without forethought.
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
// error messages.
private List permanentlyAbstractMethods = new ArrayList();
/**
* This method returns an Iterator of all abstract methods
* in our superclasses which we are unable to implement.
*/
protected Iterator getPermanentlyAbstractMethods() {
// This method can only be called after collectInheritedMethods.
if (allMethods == null) {
throw new CompilerError("isPermanentlyAbstract() called early");
}
return permanentlyAbstractMethods.iterator();
}
/**
* A flag used by turnOffInheritanceChecks() to indicate if
* inheritance checks are on or off.
*/
protected static boolean doInheritanceChecks = true;
/**
* This is a workaround to allow javadoc to turn off certain
* inheritance/override checks which interfere with javadoc
* badly. In the future it might be good to eliminate the
* shared sources of javadoc and javac to avoid the need for this
* sort of workaround.
*/
public static void turnOffInheritanceChecks() {
doInheritanceChecks = false;
}
/**
* Add all of the methods declared in or above `parent' to
* `allMethods', the set of methods in the current class.
* `myMethods' is the set of all methods declared in this
* class, and `mirandaMethods' is a repository for Miranda methods.
* If mirandaMethods is null, no mirandaMethods will be
* generated.
*
* For a definition of Miranda methods, see the comment above the
* method addMirandaMethods() which occurs later in this file.
*/
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
// when we walked our superclass lineage.
if (method.isPrivate() ||
method.isConstructor() ||
(pClass.isInterface() && !method.isAbstract())) {
continue;
}
// Get the components of the methods' signature.
Identifier name = method.getName();
Type type = method.getType();
// Check for a method of the same signature which
// was locally declared.
MemberDefinition override =
myMethods.lookupSig(name, type);
// Is this method inaccessible due to package-private
// visibility?
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
// BinaryClass, for example.
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
// non-abstract.
if (method.isAbstract()) {
permanentlyAbstractMethods.add(method);
}
// `method' is inaccessible. We do not inherit it.
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
// to `allMethods' already?
MemberDefinition formerMethod =
allMethods.lookupSig(name, type);
// If the previous definition is nonexistent or
// ignorable, replace it.
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
// VM.
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.
// Add `method' to `allMethods'.
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
// be proper by our superinterface.
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
// next method.
continue;
}
if (formerMethod.couldOverride(env, method)) {
// Do nothing. The current definition
// is specific enough.
//System.out.println("trivial meet of " +
// method);
continue;
}
if (method.couldOverride(env, formerMethod)) {
// `method' is more specific than
// `formerMethod'. replace `formerMethod'.
//System.out.println("new def of " + method);
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
// VM.
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
// a message with a helpful workaround.
env.error(this.where,
"nontrivial.meet", method,
formerMethod.getClassDefinition(),
method.getClassDeclaration()
);
}
}
}
} catch (ClassNotFound ee) {
env.error(getWhere(), "class.not.found", ee.name, this);
}
}
/**
* <p>Collect all methods defined in this class or inherited from
* any of our superclasses or interfaces. Look for any
* incompatible definitions.
*
* <p>This function is also responsible for collecting the
* <em>Miranda</em> methods for a class. For a definition of
* Miranda methods, see the comment in addMirandaMethods()
* below.
*/
protected void collectInheritedMethods(Environment env) {
// The methods defined in this class.
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
// do it again.
return;
} else {
// We have run into a circular need to collect our methods.
// This should not happen at this stage.
throw new CompilerError("collectInheritedMethods()");
}
}
myMethods = new MethodSet();
allMethods = new MethodSet();
// For testing, do not generate miranda methods.
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) {
// We only collect methods. Initializers are not relevant.
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
// superclass to our list of unimplementable methods.
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
// will be no code generated for them.
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);
}
}
////////////////////////////////////////////////////////////
/**
* Get an Iterator of all methods which could be accessed in an
* instance of this class.
*/
public Iterator getMethods(Environment env) {
if (allMethods == null) {
collectInheritedMethods(env);
}
return getMethods();
}
/**
* Get an Iterator of all methods which could be accessed in an
* instance of this class. Throw a compiler error if we haven't
* generated this information yet.
*/
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.
/**
* Add a list of methods to this class as miranda methods. This
* gets overridden with a meaningful implementation in SourceClass.
* BinaryClass should not need to do anything -- it should already
* have its miranda methods and, if it doesn't, then that doesn't
* affect our compilation.
*/
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) {
}
/**
* Look up an inner class name, from somewhere inside this class.
* Since supers and outers are in scope, search them too.
* <p>
* If no inner class is found, env.resolveName() is then called,
* to interpret the ambient package and import directives.
* <p>
* This routine operates on a "best-efforts" basis. If
* at some point a class is not found, the partially-resolved
* identifier is returned. Eventually, someone else has to
* try to get the ClassDefinition and diagnose the ClassNotFound.
* <p>
* resolveName() looks at surrounding scopes, and hence
* pulling in both inherited and uplevel types. By contrast,
* resolveInnerClass() is intended only for interpreting
* explicitly qualified names, and so look only at inherited
* types. Also, resolveName() looks for package prefixes,
* which appear similar to "very uplevel" outer classes.
* <p>
* A similar (but more complex) name-lookup process happens
* when field and identifier expressions denoting qualified names
* are type-checked. The added complexity comes from the fact
* that variables may occur in such names, and take precedence
* over class and package names.
* <p>
* In the expression type-checker, resolveInnerClass() is paralleled
* by code in FieldExpression.checkAmbigName(), which also calls
* ClassDefinition.getInnerClass() to interpret names of the form
* "OuterClass.Inner" (and also outerObject.Inner). The checking
* of an identifier expression that fails to be a variable is referred
* directly to resolveName().
*/
public Identifier resolveName(Environment env, Identifier name) {
if (tracing) env.dtEvent("ClassDefinition.resolveName: " + name);
// This logic is pretty much exactly parallel to that of
// Environment.resolveName().
if (name.isQualified()) {
// Try to resolve the first identifier component,
// because inner class names take precedence over
// package prefixes. (Cf. Environment.resolveName.)
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.
// (part of solution for 4059855).
return rhead;
}
if (!env.classExists(rhead)) {
return env.resolvePackageQualifiedName(name);
}
try {
return env.getClassDefinition(rhead).
resolveInnerClass(env, name.getTail());
} catch (ClassNotFound ee) {
// return partially-resolved name someone else can fail on
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();
}
}
// Look for an unqualified name in enclosing scopes.
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
// any enclosing local of the same name.
return f.getInnerClass().getName();
}
} catch (ClassNotFound ee) {
// a missing superclass, or something catastrophic
}
}
// No uplevel member found, so use the enclosing local if one was found.
if (lf != null) {
return lf.getInnerClass().getName();
}
// look in imports, etc.
return env.resolveName(name);
}
/**
* Interpret a qualified class name, which may have further subcomponents..
* Follow inheritance links, as in:
* class C { class N { } } class D extends C { } ... new D.N() ...
* Ignore outer scopes and packages.
* @see resolveName
*/
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 partially-resolved name someone else can fail on
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
}
// Fake a good name for a diagnostic.
return Identifier.lookupInner(this.getName(), nm);
}
}
/**
* While resolving import directives, the question has arisen:
* does a given inner class exist? If the top-level class exists,
* we ask it about an inner class via this method.
* This method looks only at the literal name of the class,
* and does not attempt to follow inheritance links.
* This is necessary, since at the time imports are being
* processed, inheritance links have not been resolved yet.
* (Thus, an import directive must always spell a class
* name exactly.)
*/
public boolean innerClassExists(Identifier nm) {
for (MemberDefinition field = getFirstMatch(nm.getHead()) ; field != null ; field = field.getNextMatch()) {
if (field.isInnerClass()) {
if (field.getInnerClass().isLocal()) {
continue; // ignore this name; it is internally generated
}
return !nm.isQualified() ||
field.getInnerClass().innerClassExists(nm.getTail());
}
}
return false;
}
/**
* Find any method with a given name.
*/
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;
}
}
// look in the super class
ClassDeclaration sup = getSuperClass();
if (sup == null)
return null;
return sup.getClassDefinition(env).findAnyMethod(env, nm);
}
/**
* Given the fact that this class has no method "nm" matching "argTypes",
* find out if the mismatch can be blamed on a particular actual argument
* which disagrees with all of the overloadings.
* If so, return the code (i<<2)+(castOK<<1)+ambig, where
* "i" is the number of the offending argument, and
* "castOK" is 1 if a cast could fix the problem.
* The target type for the argument is returned in margTypeResult[0].
* If not all methods agree on this type, "ambig" is 1.
* If there is more than one method, the choice of target type is
* arbitrary.<p>
* Return -1 if every argument is acceptable to at least one method.
* Return -2 if there are no methods of the required arity.
* The value "start" gives the index of the first argument to begin
* checking.
*/
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 {
// look in the current class
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;
}
}
}
// constructors are not inherited
if (nm.equals(idInit)) {
return haveOne;
}
// look in the super class
ClassDeclaration sup = getSuperClass();
if (sup != null) {
if (sup.getClassDefinition(env).diagnoseMismatch(env, nm, argTypes, start,
haveMatch, margType))
haveOne = true;
}
return haveOne;
}
/**
* Add a field (no checks)
*/
public void addMember(MemberDefinition field) {
//System.out.println("ADD = " + field);
if (firstMember == null) {
firstMember = lastMember = field;
} else if (field.isSynthetic() && field.isFinal()
&& field.isVariable()) {
// insert this at the front, because of initialization order
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);
}
/**
* Add a field (subclasses make checks)
*/
public void addMember(Environment env, MemberDefinition field) {
addMember(field);
if (resolved) {
// a late addition
field.resolveTypeStructure(env);
}
}
/**
* Find or create an uplevel reference for the given target.
*/
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;
}
/**
* Return the list of all uplevel references.
*/
public UplevelReference getReferences() {
return references;
}
/**
* Return the same value as getReferences.
* Also, mark the set of references frozen.
* After that, it is an error to add new references.
*/
public UplevelReference getReferencesFrozen() {
referencesFrozen = true;
return references;
}
/**
* assertion check
*/
public final void referencesMustNotBeFrozen() {
if (referencesFrozen) {
throw new CompilerError("referencesMustNotBeFrozen "+this);
}
}
/**
* Get helper method for class literal lookup.
*/
public MemberDefinition getClassLiteralLookup(long fwhere) {
throw new CompilerError("binary class");
}
/**
* Add a dependency
*/
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);
}
/**
* Print for debugging
*/
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("}");
}
/**
* Convert to String
*/
public String toString() {
return getClassDeclaration().toString();
}
/**
* After the class has been written to disk, try to free up
* some storage.
*/
public void cleanup(Environment env) {
if (env.dump()) {
env.output("[cleanup " + getName() + "]");
}
for (MemberDefinition f = getFirstMember() ; f != null ; f = f.getNextMember()) {
f.cleanup(env);
}
// keep "references" around, for the sake of local subclasses
documentation = null;
}
}