1282 lines · 51.6 KB · Java
RawDownload
/* * Copyright (c) 1994, 2003, 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.tree; import sun.tools.java.*;import sun.tools.asm.*;import java.io.PrintStream;import java.util.Hashtable; /** * 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. */publicclass FieldExpression extends UnaryExpression {    Identifier id;    MemberDefinition field;    Expression implementation;     // The class from which the field is select ed.    ClassDefinition clazz;     // For an expression of the form '<class>.super', then    // this is <class>, else null.    private ClassDefinition superBase;     /**     * constructor     */    public FieldExpression(long where, Expression right, Identifier id) {        super(FIELD, where, Type.tError, right);        this.id = id;    }    public FieldExpression(long where, Expression right, MemberDefinition field) {        super(FIELD, where, field.getType(), right);        this.id = field.getName();        this.field = field;    }     public Expression getImplementation() {        if (implementation != null)            return implementation;        return this;    }     /**     * Return true if the field is being selected from     * a qualified 'super'.     */    private boolean isQualSuper() {        return superBase != null;    }     /**     * Convert an '.' expression to a qualified identifier     */    static public Identifier toIdentifier(Expression e) {        StringBuffer buf = new StringBuffer();        while (e.op == FIELD) {            FieldExpression fe = (FieldExpression)e;            if (fe.id == idThis || fe.id == idClass) {                return null;            }            buf.insert(0, fe.id);            buf.insert(0, '.');            e = fe.right;        }        if (e.op != IDENT) {            return null;        }        buf.insert(0, ((IdentifierExpression)e).id);        return Identifier.lookup(buf.toString());    }     /**     * Convert a qualified name into a type.     * Performs a careful check of each inner-class component,     * including the JLS 6.6.1 access checks that were omitted     * in 'FieldExpression.toType'.     * <p>     * This code is similar to 'checkCommon', which could be cleaned     * up a bit long the lines we have done here.     */    /*-------------------------------------------------------*    Type toQualifiedType(Environment env, Context ctx) {        ClassDefinition ctxClass = ctx.field.getClassDefinition();        Type rty = right.toQualifiedType(env, ctx);        if (rty == Type.tPackage) {            // Is this field expression a non-inner type?            Identifier nm = toIdentifier(this);            if ((nm != null) && env.classExists(nm)) {                Type t = Type.tClass(nm);                if (env.resolve(where, ctxClass, t)) {                    return t;                } else {                    return null;                }            }            // Not a type.  Must be a package prefix.            return Type.tPackage;        }        if (rty == null) {            // An error was already reported, so quit.            return null;        }         // Check inner-class qualification while unwinding from recursion.        try {            ClassDefinition rightClass = env.getClassDefinition(rty);             // Local variables, which cannot be inner classes,            // are ignored here, and thus will not hide inner            // classes.  Is this correct?            MemberDefinition field = rightClass.getInnerClass(env, id);            if (field == null) {                env.error(where, "inner.class.expected", id, rightClass);                return Type.tError;            }             ClassDefinition innerClass = field.getInnerClass();            Type t = innerClass.getType();             if (!ctxClass.canAccess(env, field)) {                env.error(where, "no.type.access", id, rightClass, ctxClass);                return t;            }            if (field.isProtected()                && !ctxClass.protectedAccess(env, field, rty)) {                env.error(where, "invalid.protected.type.use", id, ctxClass, rty);                return t;            }             // These were ommitted earlier in calls to 'toType', but I can't            // see any reason for that.  I think it was an oversight.  See            // 'checkCommon' and 'checkInnerClass'.            innerClass.noteUsedBy(ctxClass, where, env);            ctxClass.addDependency(field.getClassDeclaration());             return t;         } catch (ClassNotFound e) {            env.error(where, "class.not.found", e.name, ctx.field);        }         // Class not found.        return null;    }    *-------------------------------------------------------*/     /**     * Convert an '.' expression to a type     */     // This is a rewrite to treat qualified names in a    // context in which a type name is expected in the    // same way that they are handled for an ambiguous    // or expression-expected context in 'checkCommon'    // below.  The new code is cleaner and allows better    // localization of errors.  Unfortunately, most    // qualified names appearing in types are actually    // handled by 'Environment.resolve'.  There isn't    // much point, then, in breaking out 'toType' as a    // special case until the other cases can be cleaned    // up as well.  For the time being, we will leave this    // code disabled, thus reducing the testing requirements.    /*-------------------------------------------------------*    Type toType(Environment env, Context ctx) {        Type t = toQualifiedType(env, ctx);        if (t == null) {            return Type.tError;        }        if (t == Type.tPackage) {            FieldExpression.reportFailedPackagePrefix(env, right, true);            return Type.tError;        }        return t;    }    *-------------------------------------------------------*/     Type toType(Environment env, Context ctx) {        Identifier id = toIdentifier(this);        if (id == null) {            env.error(where, "invalid.type.expr");            return Type.tError;        }        Type t = Type.tClass(ctx.resolveName(env, id));        if (env.resolve(where, ctx.field.getClassDefinition(), t)) {            return t;        }        return Type.tError;    }     /**     * Check if the present name is part of a scoping prefix.     */     public Vset checkAmbigName(Environment env, Context ctx,                               Vset vset, Hashtable exp,                               UnaryExpression loc) {        if (id == idThis || id == idClass) {            loc = null;         // this cannot be a type or package        }        return checkCommon(env, ctx, vset, exp, loc, false);    }     /**     * Check the expression     */     public Vset checkValue(Environment env, Context ctx,                           Vset vset, Hashtable exp) {        vset = checkCommon(env, ctx, vset, exp, null, false);        if (id == idSuper && type != Type.tError) {            // "super" is not allowed in this context.            // It must always qualify another name.            env.error(where, "undef.var.super", idSuper);        }        return vset;    }     /**     * If 'checkAmbiguousName' returns 'Package.tPackage', then it was     * unable to resolve any prefix of the qualified name.  This method     * attempts to diagnose the problem.     */     static void reportFailedPackagePrefix(Environment env, Expression right) {        reportFailedPackagePrefix(env, right, false);    }     static void reportFailedPackagePrefix(Environment env,                                          Expression right,                                          boolean mustBeType) {        // Find the leftmost component, and put the blame on it.        Expression idp = right;        while (idp instanceof UnaryExpression)            idp = ((UnaryExpression)idp).right;        IdentifierExpression ie = (IdentifierExpression)idp;         // It may be that 'ie' refers to an ambiguous class.  Check this        // with a call to env.resolve(). Part of solution for 4059855.        try {            env.resolve(ie.id);        } catch (AmbiguousClass e) {            env.error(right.where, "ambig.class", e.name1, e.name2);            return;        } catch (ClassNotFound e) {        }         if (idp == right) {            if (mustBeType) {                env.error(ie.where, "undef.class", ie.id);            } else {                env.error(ie.where, "undef.var.or.class", ie.id);            }        } else {            if (mustBeType) {                env.error(ie.where, "undef.class.or.package", ie.id);            } else {                env.error(ie.where, "undef.var.class.or.package", ie.id);            }        }    }     /**     * Rewrite accesses to private fields of another class.     */     private Expression    implementFieldAccess(Environment env, Context ctx, Expression base, boolean isLHS) {        ClassDefinition abase = accessBase(env, ctx);        if (abase != null) {             // If the field is final and its initializer is a constant expression,            // then just rewrite to the constant expression. This is not just an            // optimization, but is required for correctness.  If an expression is            // rewritten to use an access method, then its status as a constant            // expression is lost.  This was the cause of bug 4098737.  Note that            // a call to 'getValue(env)' below would not be correct, as it attempts            // to simplify the initial value expression, which must not occur until            // after the checking phase, for example, after definite assignment checks.            if (field.isFinal()) {                Expression e = (Expression)field.getValue();                // Must not be LHS here.  Test as a precaution,                // as we may not be careful to avoid this when                // compiling an erroneous program.                if ((e != null) && e.isConstant() && !isLHS) {                    return e.copyInline(ctx);                }            }             //System.out.println("Finding access method for " + field);            MemberDefinition af = abase.getAccessMember(env, ctx, field, isQualSuper());            //System.out.println("Using access method " + af);             if (!isLHS) {                //System.out.println("Reading " + field +                //                              " via access method " + af);                // If referencing the value of the field, then replace                // with a call to the access method.  If assigning to                // the field, a call to the update method will be                // generated later. It is important that                // 'implementation' not be set to non-null if the                // expression is a valid assignment target.                // (See 'checkLHS'.)                if (field.isStatic()) {                    Expression args[] = { };                    Expression call =                        new MethodExpression(where, null, af, args);                    return new CommaExpression(where, base, call);                } else {                    Expression args[] = { base };                    return new MethodExpression(where, null, af, args);                }            }        }         return null;    }     /**     * Determine if an access method is required, and, if so, return     * the class in which it should appear, else return null.     */    private ClassDefinition accessBase(Environment env, Context ctx) {        if (field.isPrivate()) {            ClassDefinition cdef = field.getClassDefinition();            ClassDefinition ctxClass = ctx.field.getClassDefinition();            if (cdef == ctxClass){                // If access from same class as field, then no access                // method is needed.                return null;            }            // An access method is needed in the class containing the field.            return cdef;        } else if (field.isProtected()) {            if (superBase == null) {                // If access is not via qualified super, then it is either                // OK without an access method, or it is an illegal access                // for which an error message should have been issued.                // Legal accesses include unqualified 'super.foo'.                return null;            }            ClassDefinition cdef = field.getClassDefinition();            ClassDefinition ctxClass = ctx.field.getClassDefinition();            if (cdef.inSamePackage(ctxClass)) {                // Access to protected member in same package always allowed.                return null;            }            // Access via qualified super.            // An access method is needed in the qualifying class, an            // immediate subclass of the class containing the selected            // field.  NOTE: The fact that the returned class is 'superBase'            // carries the additional bit of information (that a special            // superclass access method is being created) which is provided            // to 'getAccessMember' via its 'isSuper' argument.            return superBase;        } else {            // No access method needed.            return null;        }    }     /**     * Determine if a type is accessible from a given class.     */    static boolean isTypeAccessible(long where,                                    Environment env,                                    Type t,                                    ClassDefinition c) {        switch (t.getTypeCode()) {          case TC_CLASS:            try {                Identifier nm = t.getClassName();                // Why not just use 'Environment.getClassDeclaration' here?                // But 'Environment.getClassDeclation' has special treatment                // for local classes that is probably necessary.  This code                // was adapted from 'Environment.resolve'.                ClassDefinition def = env.getClassDefinition(t);                return c.canAccess(env, def.getClassDeclaration());            } catch (ClassNotFound e) {}  // Ignore -- reported elsewhere.            return true;          case TC_ARRAY:            return isTypeAccessible(where, env, t.getElementType(), c);          default:            return true;        }    }     /**     * Common code for checkValue and checkAmbigName     */     private Vset checkCommon(Environment env, Context ctx,                             Vset vset, Hashtable exp,                             UnaryExpression loc, boolean isLHS) {         // Handle class literal, e.g., 'x.class'.        if (id == idClass) {             // In 'x.class', 'x' must be a type name, possibly qualified.            Type t = right.toType(env, ctx);             if (!t.isType(TC_CLASS) && !t.isType(TC_ARRAY)) {                if (t.isType(TC_ERROR)) {                    type = Type.tClassDesc;                    return vset;                }                String wrc = null;                switch (t.getTypeCode()) {                  case TC_VOID: wrc = "Void"; break;                  case TC_BOOLEAN: wrc = "Boolean"; break;                  case TC_BYTE: wrc = "Byte"; break;                  case TC_CHAR: wrc = "Character"; break;                  case TC_SHORT: wrc = "Short"; break;                  case TC_INT: wrc = "Integer"; break;                  case TC_FLOAT: wrc = "Float"; break;                  case TC_LONG: wrc = "Long"; break;                  case TC_DOUBLE: wrc = "Double"; break;                  default:                      env.error(right.where, "invalid.type.expr");                      return vset;                }                Identifier wid = Identifier.lookup(idJavaLang+"."+wrc);                Expression wcls = new TypeExpression(where, Type.tClass(wid));                implementation = new FieldExpression(where, wcls, idTYPE);                vset = implementation.checkValue(env, ctx, vset, exp);                type = implementation.type; // java.lang.Class                return vset;            }             // Check for the bogus type `array of void'            if (t.isVoidArray()) {                type = Type.tClassDesc;                env.error(right.where, "void.array");                return vset;            }             // it is a class or array            long fwhere = ctx.field.getWhere();            ClassDefinition fcls = ctx.field.getClassDefinition();            MemberDefinition lookup = fcls.getClassLiteralLookup(fwhere);             String sig = t.getTypeSignature();            String className;            if (t.isType(TC_CLASS)) {                // sig is like "Lfoo/bar;", name is like "foo.bar".                // We assume SIG_CLASS and SIG_ENDCLASS are 1 char each.                className = sig.substring(1, sig.length()-1)                    .replace(SIGC_PACKAGE, '.');            } else {                // sig is like "[Lfoo/bar;" or "[I";                // name is like "[Lfoo.bar" or (again) "[I".                className = sig.replace(SIGC_PACKAGE, '.');            }             if (fcls.isInterface()) {                // The immediately-enclosing type is an interface.                // The class literal can only appear in an initialization                // expression, so don't bother caching it.  (This could                // lose if many initializations use the same class literal,                // but saves time and code space otherwise.)                implementation =                    makeClassLiteralInlineRef(env, ctx, lookup, className);            } else {                // Cache the call to the helper, as it may be executed                // many times (e.g., if the class literal is inside a loop).                ClassDefinition inClass = lookup.getClassDefinition();                MemberDefinition cfld =                    getClassLiteralCache(env, ctx, className, inClass);                implementation =                    makeClassLiteralCacheRef(env, ctx, lookup, cfld, className);            }             vset = implementation.checkValue(env, ctx, vset, exp);            type = implementation.type; // java.lang.Class

Lines 1–500 of 1282.