2674 lines · 109.2 KB · Java
RawDownload
     static String classModifierString(int mods) {        String s = "";        for (int i = 0; i < classModifierBits.length; i++) {            if ((mods & classModifierBits[i]) != 0) {                s = s + " " + classModifierNames[i];                mods &= ~classModifierBits[i];            }        }        if (mods != 0) {            s = s + " ILLEGAL:" + Integer.toHexString(mods);        }        return s;    }     /**     * 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) {        return getAccessMember(env, ctx, field, false, isSuper);    }     public MemberDefinition getUpdateMember(Environment env, Context ctx,                                          MemberDefinition field, boolean isSuper) {        if (!field.isVariable()) {            throw new CompilerError("method");        }        return getAccessMember(env, ctx, field, true, isSuper);    }     private MemberDefinition getAccessMember(Environment env, Context ctx,                                             MemberDefinition field,                                             boolean isUpdate,                                             boolean isSuper) {         // The 'isSuper' argument is really only meaningful when the        // target member is a method, in which case an 'invokespecial'        // is needed.  For fields, 'getfield' and 'putfield' instructions        // are generated in either case, and 'isSuper' currently plays        // no essential role.  Nonetheless, we maintain the distinction        // consistently for the time being.         boolean isStatic = field.isStatic();        boolean isMethod = field.isMethod();         // Find pre-existing access method.        // In the case of a field access method, we only look for the getter.        // A getter is always created whenever a setter is.        // QUERY: Why doesn't the 'MemberDefinition' object for the field        // itself just have fields for its getter and setter?        MemberDefinition af;        for (af = getFirstMember(); af != null; af = af.getNextMember()) {            if (af.getAccessMethodTarget() == field) {                if (isMethod && af.isSuperAccessMethod() == isSuper) {                    break;                }                // Distinguish the getter and the setter by the number of                // arguments.                int nargs = af.getType().getArgumentTypes().length;                // This was (nargs == (isStatic ? 0 : 1) + (isUpdate ? 1 : 0))                // in order to find a setter as well as a getter.  This caused                // allocation of multiple getters.                if (nargs == (isStatic ? 0 : 1)) {                    break;                }            }        }         if (af != null) {            if (!isUpdate) {                return af;            } else {                MemberDefinition uf = af.getAccessUpdateMember();                if (uf != null) {                    return uf;                }            }        } else if (isUpdate) {            // must find or create the getter before creating the setter            af = getAccessMember(env, ctx, field, false, isSuper);        }         // If we arrive here, we are creating a new access member.         Identifier anm;        Type dummyType = null;         if (field.isConstructor()) {            // For a constructor, we use the same name as for all            // constructors ("<init>"), but add a distinguishing            // argument of an otherwise unused "dummy" type.            anm = idInit;            // Get the dummy class, creating it if necessary.            SourceClass outerMostClass = (SourceClass)getTopClass();            dummyType = outerMostClass.dummyArgumentType;            if (dummyType == null) {                // Create dummy class.                IdentifierToken sup =                    new IdentifierToken(0, idJavaLangObject);                IdentifierToken interfaces[] = {};                IdentifierToken t = new IdentifierToken(0, idNull);                int mod = M_ANONYMOUS | M_STATIC | M_SYNTHETIC;                // If an interface has a public inner class, the dummy class for                // the constructor must always be accessible. Fix for 4221648.                if (outerMostClass.isInterface()) {                    mod |= M_PUBLIC;                }                ClassDefinition dummyClass =                    toplevelEnv.makeClassDefinition(toplevelEnv,                                                    0, t, null, mod,                                                    sup, interfaces,                                                    outerMostClass);                // Check the class.                // It is likely that a full check is not really necessary,                // but it is essential that the class be marked as parsed.                dummyClass.getClassDeclaration().setDefinition(dummyClass, CS_PARSED);                Expression argsX[] = {};                Type argTypesX[] = {};                try {                    ClassDefinition supcls =                        toplevelEnv.getClassDefinition(idJavaLangObject);                    dummyClass.checkLocalClass(toplevelEnv, null,                                               new Vset(), supcls, argsX, argTypesX);                } catch (ClassNotFound ee) {};                // Get class type.                dummyType = dummyClass.getType();                outerMostClass.dummyArgumentType = dummyType;            }        } else {            // Otherwise, we use the name "access$N", for the            // smallest value of N >= 0 yielding an unused name.            for (int i = 0; ; i++) {                anm = Identifier.lookup(prefixAccess + i);                if (getFirstMatch(anm) == null) {                    break;                }            }        }         Type argTypes[];        Type t = field.getType();         if (isStatic) {            if (!isMethod) {                if (!isUpdate) {                    Type at[] = { };                    argTypes = at;                    t = Type.tMethod(t); // nullary getter                } else {                    Type at[] = { t };                    argTypes = at;                    t = Type.tMethod(Type.tVoid, argTypes); // unary setter                }            } else {                // Since constructors are never static, we don't                // have to worry about a dummy argument here.                argTypes = t.getArgumentTypes();            }        } else {            // All access methods for non-static members get an explicit            // 'this' pointer as an extra argument, as the access methods            // themselves must be static. EXCEPTION: Access methods for            // constructors are non-static.            Type classType = this.getType();            if (!isMethod) {                if (!isUpdate) {                    Type at[] = { classType };                    argTypes = at;                    t = Type.tMethod(t, argTypes); // nullary getter                } else {                    Type at[] = { classType, t };                    argTypes = at;                    t = Type.tMethod(Type.tVoid, argTypes); // unary setter                }            } else {                // Target is a method, possibly a constructor.                Type at[] = t.getArgumentTypes();                int nargs = at.length;                if (field.isConstructor()) {                    // Access method is a constructor.                    // Requires a dummy argument.                    MemberDefinition outerThisArg =                        ((SourceMember)field).getOuterThisArg();                    if (outerThisArg != null) {                        // Outer instance link must be the first argument.                        // The following is a sanity check that will catch                        // most cases in which in this requirement is violated.                        if (at[0] != outerThisArg.getType()) {                            throw new CompilerError("misplaced outer this");                        }                        // Strip outer 'this' argument.                        // It will be added back when the access method is checked.                        argTypes = new Type[nargs];                        argTypes[0] = dummyType;                        for (int i = 1; i < nargs; i++) {                            argTypes[i] = at[i];                        }                    } else {                        // There is no outer instance.                        argTypes = new Type[nargs+1];                        argTypes[0] = dummyType;                        for (int i = 0; i < nargs; i++) {                            argTypes[i+1] = at[i];                        }                    }                } else {                    // Access method is static.                    // Requires an explicit 'this' argument.                    argTypes = new Type[nargs+1];                    argTypes[0] = classType;                    for (int i = 0; i < nargs; i++) {                        argTypes[i+1] = at[i];                    }                }                t = Type.tMethod(t.getReturnType(), argTypes);            }        }         int nlen = argTypes.length;        long where = field.getWhere();        IdentifierToken names[] = new IdentifierToken[nlen];        for (int i = 0; i < nlen; i++) {            names[i] = new IdentifierToken(where, Identifier.lookup("$"+i));        }         Expression access = null;        Expression thisArg = null;        Expression args[] = null;         if (isStatic) {            args = new Expression[nlen];            for (int i = 0 ; i < nlen ; i++) {                args[i] = new IdentifierExpression(names[i]);            }        } else {            if (field.isConstructor()) {                // Constructor access method is non-static, so                // 'this' works normally.                thisArg = new ThisExpression(where);                // Remove dummy argument, as it is not                // passed to the target method.                args = new Expression[nlen-1];                for (int i = 1 ; i < nlen ; i++) {                    args[i-1] = new IdentifierExpression(names[i]);                }            } else {                // Non-constructor access method is static, so                // we use the first argument as 'this'.                thisArg = new IdentifierExpression(names[0]);                // Remove first argument.                args = new Expression[nlen-1];                for (int i = 1 ; i < nlen ; i++) {                    args[i-1] = new IdentifierExpression(names[i]);                }            }            access = thisArg;        }         if (!isMethod) {            access = new FieldExpression(where, access, field);            if (isUpdate) {                access = new AssignExpression(where, access, args[0]);            }        } else {            // If true, 'isSuper' forces a non-virtual call.            access = new MethodExpression(where, access, field, args, isSuper);        }         Statement code;        if (t.getReturnType().isType(TC_VOID)) {            code = new ExpressionStatement(where, access);        } else {            code = new ReturnStatement(where, access);        }        Statement body[] = { code };        code = new CompoundStatement(where, body);         // Access methods are now static (constructors excepted), and no longer final.        // This change was mandated by the interaction of the access method        // naming conventions and the restriction against overriding final        // methods.        int mod = M_SYNTHETIC;        if (!field.isConstructor()) {            mod |= M_STATIC;        }         // Create the synthetic method within the class in which the referenced        // private member appears.  The 'env' argument to 'makeMemberDefinition'        // is suspect because it represents the environment at the point at        // which a reference takes place, while it should represent the        // environment in which the definition of the synthetic method appears.        // We get away with this because 'env' is used only to access globals        // such as 'Environment.error', and also as an argument to        // 'resolveTypeStructure', which immediately discards it using        // 'setupEnv'. Apparently, the current definition of 'setupEnv'        // represents a design change that has not been thoroughly propagated.        // An access method is declared with same list of exceptions as its        // target. As the exceptions are simply listed by name, the correctness        // of this approach requires that the access method be checked        // (name-resolved) in the same context as its target method  This        // should always be the case.        SourceMember newf = (SourceMember)            env.makeMemberDefinition(env, where, this,                                     null, mod, t, anm, names,                                     field.getExceptionIds(), code);        // Just to be safe, copy over the name-resolved exceptions from the        // target so that the context in which the access method is checked        // doesn't matter.        newf.setExceptions(field.getExceptions(env));         newf.setAccessMethodTarget(field);        if (isUpdate) {            af.setAccessUpdateMember(newf);        }        newf.setIsSuperAccessMethod(isSuper);         // The call to 'check' is not needed, as the access method will be        // checked by the containing class after it is added.  This is the        // idiom followed in the implementation of class literals. (See        // 'FieldExpression.java'.) In any case, the context is wrong in the        // call below.  The access method must be checked in the context in        // which it is declared, i.e., the class containing the referenced        // private member, not the (inner) class in which the original member        // reference occurs.        //        // try {        //     newf.check(env, ctx, new Vset());        // } catch (ClassNotFound ee) {        //     env.error(where, "class.not.found", ee.name, this);        // }         // The comment above is inaccurate.  While it is often the case        // that the containing class will check the access method, this is        // by no means guaranteed.  In fact, an access method may be added        // after the checking of its class is complete.  In this case, however,        // the context in which the class was checked will have been saved in        // the class definition object (by the fix for 4095716), allowing us        // to check the field now, and in the correct context.        // This fixes bug 4098093.         Context checkContext = newf.getClassDefinition().getClassContext();        if (checkContext != null) {            //System.out.println("checking late addition: " + this);            try {                newf.check(env, checkContext, new Vset());            } catch (ClassNotFound ee) {                env.error(where, "class.not.found", ee.name, this);            }        }          //System.out.println("[Access member '" +        //                      newf + "' created for field '" +        //                      field +"' in class '" + this + "']");         return newf;    }     /**     * Find an inner class of 'this', chosen arbitrarily.     * Result is always an actual class, never an interface.     * Returns null if none found.     */    SourceClass findLookupContext() {        // Look for an immediate inner class.        for (MemberDefinition f = getFirstMember();             f != null;             f = f.getNextMember()) {            if (f.isInnerClass()) {                SourceClass ic = (SourceClass)f.getInnerClass();                if (!ic.isInterface()) {                    return ic;                }            }        }        // Look for a class nested within an immediate inner interface.        // At this point, we have given up on finding a minimally-nested        // class (which would require a breadth-first traversal).  It doesn't        // really matter which inner class we find.        for (MemberDefinition f = getFirstMember();             f != null;             f = f.getNextMember()) {            if (f.isInnerClass()) {                SourceClass lc =                    ((SourceClass)f.getInnerClass()).findLookupContext();                if (lc != null) {                    return lc;                }            }        }        // No inner classes.        return null;    }     private MemberDefinition lookup = null;     /**     * Get helper method for class literal lookup.     */    public MemberDefinition getClassLiteralLookup(long fwhere) {         // If we have already created a lookup method, reuse it.        if (lookup != null) {            return lookup;        }         // If the current class is a nested class, make sure we put the        // lookup method in the outermost class.  Set 'lookup' for the        // intervening inner classes so we won't have to do the search        // again.        if (outerClass != null) {            lookup = outerClass.getClassLiteralLookup(fwhere);            return lookup;        }         // If we arrive here, there was no existing 'class$' method.         ClassDefinition c = this;        boolean needNewClass = false;         if (isInterface()) {            // The top-level type is an interface.  Try to find an existing            // inner class in which to create the helper method.  Any will do.            c = findLookupContext();            if (c == null) {                // The interface has no inner classes.  Create an anonymous                // inner class to hold the helper method, as an interface must                // not have any methods.  The tests above for prior creation                // of a 'class$' method assure that only one such class is                // allocated for each outermost class containing a class                // literal embedded somewhere within.  Part of fix for 4055017.                needNewClass = true;                IdentifierToken sup =                    new IdentifierToken(fwhere, idJavaLangObject);                IdentifierToken interfaces[] = {};                IdentifierToken t = new IdentifierToken(fwhere, idNull);                int mod = M_PUBLIC | M_ANONYMOUS | M_STATIC | M_SYNTHETIC;                c = (SourceClass)                    toplevelEnv.makeClassDefinition(toplevelEnv,                                                    fwhere, t, null, mod,                                                    sup, interfaces, this);            }        }          // The name of the class-getter stub is "class$"        Identifier idDClass = Identifier.lookup(prefixClass);        Type strarg[] = { Type.tString };         // Some sanity checks of questionable value.        //        // This check became useless after matchMethod() was modified        // to not return synthetic methods.        //        //try {        //    lookup = c.matchMethod(toplevelEnv, c, idDClass, strarg);        //} catch (ClassNotFound ee) {        //    throw new CompilerError("unexpected missing class");        //} catch (AmbiguousMember ee) {        //    throw new CompilerError("synthetic name clash");        //}        //if (lookup != null && lookup.getClassDefinition() == c) {        //    // Error if method found was not inherited.        //    throw new CompilerError("unexpected duplicate");        //}        // Some sanity checks of questionable value.         /*  // The helper function looks like this.         *  // It simply maps a checked exception to an unchecked one.         *  static Class class$(String class$) {         *    try { return Class.forName(class$); }         *    catch (ClassNotFoundException forName) {         *      throw new NoClassDefFoundError(forName.getMessage());         *    }         *  }         */        long w = c.getWhere();        IdentifierToken arg = new IdentifierToken(w, idDClass);        Expression e = new IdentifierExpression(arg);        Expression a1[] = { e };        Identifier idForName = Identifier.lookup("forName");        e = new MethodExpression(w, new TypeExpression(w, Type.tClassDesc),                                 idForName, a1);        Statement body = new ReturnStatement(w, e);        // map the exceptions        Identifier idClassNotFound =            Identifier.lookup("java.lang.ClassNotFoundException");        Identifier idNoClassDefFound =            Identifier.lookup("java.lang.NoClassDefFoundError");        Type ctyp = Type.tClass(idClassNotFound);        Type exptyp = Type.tClass(idNoClassDefFound);        Identifier idGetMessage = Identifier.lookup("getMessage");        e = new IdentifierExpression(w, idForName);        e = new MethodExpression(w, e, idGetMessage, new Expression[0]);        Expression a2[] = { e };        e = new NewInstanceExpression(w, new TypeExpression(w, exptyp), a2);        Statement handler = new CatchStatement(w, new TypeExpression(w, ctyp),                                               new IdentifierToken(idForName),

Lines 1501–2000 of 2674. Previous lines