| /* |
| * 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; |
| } |
| } |