Collect all methods defined in this class or inherited from * any of our superclasses or interfaces. Look for any * incompatible definitions. * *
This function is also responsible for collecting the * Miranda 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. *
* If no inner class is found, env.resolveName() is then called, * to interpret the ambient package and import directives. *
* 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. *
* 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. *
* 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. *
* 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.
* 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; } }