| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.tools.tree;  | 
 | 
 | 
 | 
import sun.tools.java.*;  | 
 | 
import sun.tools.asm.*;  | 
 | 
import java.io.PrintStream;  | 
 | 
import java.util.Hashtable;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
public  | 
 | 
class FieldExpression extends UnaryExpression { | 
 | 
    Identifier id;  | 
 | 
    MemberDefinition field;  | 
 | 
    Expression implementation;  | 
 | 
 | 
 | 
      | 
 | 
    ClassDefinition clazz;  | 
 | 
 | 
 | 
    // For an expression of the form '<class>.super', then  | 
 | 
      | 
 | 
    private ClassDefinition superBase;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    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;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private boolean isQualSuper() { | 
 | 
        return superBase != null;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    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 omitted 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;           | 
 | 
        }  | 
 | 
        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.  | 
 | 
              | 
 | 
            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) { | 
 | 
          | 
 | 
        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  | 
 | 
          | 
 | 
        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  | 
 | 
              | 
 | 
            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  | 
 | 
                  | 
 | 
                if ((e != null) && e.isConstant() && !isLHS) { | 
 | 
                    return e.copyInline(ctx);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            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.  | 
 | 
                  | 
 | 
                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;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    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  | 
 | 
                  | 
 | 
                return null;  | 
 | 
            }  | 
 | 
              | 
 | 
            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.  | 
 | 
                  | 
 | 
                return null;  | 
 | 
            }  | 
 | 
            ClassDefinition cdef = field.getClassDefinition();  | 
 | 
            ClassDefinition ctxClass = ctx.field.getClassDefinition();  | 
 | 
            if (cdef.inSamePackage(ctxClass)) { | 
 | 
                  | 
 | 
                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  | 
 | 
              | 
 | 
            return superBase;  | 
 | 
        } else { | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    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  | 
 | 
                  | 
 | 
                ClassDefinition def = env.getClassDefinition(t);  | 
 | 
                return c.canAccess(env, def.getClassDeclaration());  | 
 | 
            } catch (ClassNotFound e) {}   | 
 | 
            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) { | 
 | 
 | 
 | 
          | 
 | 
        if (id == idClass) { | 
 | 
 | 
 | 
              | 
 | 
            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;   | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            if (t.isVoidArray()) { | 
 | 
                type = Type.tClassDesc;  | 
 | 
                env.error(right.where, "void.array");  | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            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".  | 
 | 
                  | 
 | 
                className = sig.substring(1, sig.length()-1)  | 
 | 
                    .replace(SIGC_PACKAGE, '.');  | 
 | 
            } else { | 
 | 
                // sig is like "[Lfoo/bar;" or "[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,  | 
 | 
                  | 
 | 
                implementation =  | 
 | 
                    makeClassLiteralInlineRef(env, ctx, lookup, className);  | 
 | 
            } else { | 
 | 
                // Cache the call to the helper, as it may be executed  | 
 | 
                  | 
 | 
                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;   | 
 | 
            return vset;  | 
 | 
        }  | 
 | 
 | 
 | 
        // Arrive here if not a class literal.  | 
 | 
 | 
 | 
        if (field != null) { | 
 | 
 | 
 | 
            // The field as been pre-set, e.g., as the result of transforming  | 
 | 
            // an 'IdentifierExpression'. Most error-checking has already been  | 
 | 
            // performed at this point.  | 
 | 
            // QUERY: Why don't we further unify checking of identifier  | 
 | 
            // expressions and field expressions that denote instance and  | 
 | 
            // class variables?  | 
 | 
 | 
 | 
            implementation = implementFieldAccess(env, ctx, right, isLHS);  | 
 | 
            return (right == null) ?  | 
 | 
                vset : right.checkAmbigName(env, ctx, vset, exp, this);  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        vset = right.checkAmbigName(env, ctx, vset, exp, this);  | 
 | 
        if (right.type == Type.tPackage) { | 
 | 
              | 
 | 
            if (loc == null) { | 
 | 
                FieldExpression.reportFailedPackagePrefix(env, right);  | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
            // ASSERT(loc.right == this)  | 
 | 
 | 
 | 
              | 
 | 
            Identifier nm = toIdentifier(this);  | 
 | 
            if ((nm != null) && env.classExists(nm)) { | 
 | 
                loc.right = new TypeExpression(where, Type.tClass(nm));  | 
 | 
                  | 
 | 
                ClassDefinition ctxClass = ctx.field.getClassDefinition();  | 
 | 
                env.resolve(where, ctxClass, loc.right.type);  | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            type = Type.tPackage;  | 
 | 
            return vset;  | 
 | 
        }  | 
 | 
 | 
 | 
        // Good; we have a well-defined qualifier type.  | 
 | 
 | 
 | 
        ClassDefinition ctxClass = ctx.field.getClassDefinition();  | 
 | 
        boolean staticRef = (right instanceof TypeExpression);  | 
 | 
 | 
 | 
        try { | 
 | 
 | 
 | 
            // Handle array 'length' field, e.g., 'x.length'.  | 
 | 
 | 
 | 
            if (!right.type.isType(TC_CLASS)) { | 
 | 
                if (right.type.isType(TC_ARRAY) && id.equals(idLength)) { | 
 | 
                    // Verify that the type of the base expression is accessible.  | 
 | 
                      | 
 | 
                    if (!FieldExpression.isTypeAccessible(where, env, right.type, ctxClass)) { | 
 | 
                        ClassDeclaration cdecl = ctxClass.getClassDeclaration();  | 
 | 
                        if (staticRef) { | 
 | 
                            env.error(where, "no.type.access",  | 
 | 
                                      id, right.type.toString(), cdecl);  | 
 | 
                        } else { | 
 | 
                            env.error(where, "cant.access.member.type",  | 
 | 
                                      id, right.type.toString(), cdecl);  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                    type = Type.tInt;  | 
 | 
                    implementation = new LengthExpression(where, right);  | 
 | 
                    return vset;  | 
 | 
                }  | 
 | 
                if (!right.type.isType(TC_ERROR)) { | 
 | 
                    env.error(where, "invalid.field.reference", id, right.type);  | 
 | 
                }  | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
            // At this point, we know that 'right.type' is a class type.  | 
 | 
 | 
 | 
            // Note that '<expr>.super(...)' and '<expr>.this(...)' cases never  | 
 | 
            // reach here.  Instead, '<expr>' is stored as the 'outerArg' field  | 
 | 
            // of a 'SuperExpression' or 'ThisExpression' node.  | 
 | 
 | 
 | 
            // If our prefix is of the form '<class>.super', then we are  | 
 | 
            // about to do a field selection '<class>.super.<field>'.  | 
 | 
            // Save the qualifying class in 'superBase', which is non-null  | 
 | 
            // only if the current FieldExpression is a qualified 'super' form.  | 
 | 
            // Also, set 'sourceClass' to the "effective accessing class" relative  | 
 | 
            // to which access checks will be performed.  Normally, this is the  | 
 | 
            // immediately enclosing class.  For '<class>.this' and '<class>.super',  | 
 | 
            // however, we use <class>.  | 
 | 
 | 
 | 
            ClassDefinition sourceClass = ctxClass;  | 
 | 
            if (right instanceof FieldExpression) { | 
 | 
                Identifier id = ((FieldExpression)right).id;  | 
 | 
                if (id == idThis) { | 
 | 
                    sourceClass = ((FieldExpression)right).clazz;  | 
 | 
                } else if (id == idSuper) { | 
 | 
                    sourceClass = ((FieldExpression)right).clazz;  | 
 | 
                    superBase = sourceClass;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            // Handle 'class.this' and 'class.super'.  | 
 | 
            //  | 
 | 
            // Suppose 'super.name' appears within a class C with immediate  | 
 | 
            // superclass S. According to JLS 15.10.2, 'super.name' in this  | 
 | 
            // case is equivalent to '((S)this).name'.  Analogously, we interpret  | 
 | 
            // 'class.super.name' as '((S)(class.this)).name', where S is the  | 
 | 
            // immediate superclass of (enclosing) class 'class'.  | 
 | 
            // Note that 'super' may not stand alone as an expression, but must  | 
 | 
            // occur as the qualifying expression of a field access or a method  | 
 | 
            // invocation.  This is enforced in 'SuperExpression.checkValue' and  | 
 | 
            // 'FieldExpression.checkValue', and need not concern us here.  | 
 | 
 | 
 | 
              | 
 | 
            clazz = env.getClassDefinition(right.type);  | 
 | 
            if (id == idThis || id == idSuper) { | 
 | 
                if (!staticRef) { | 
 | 
                    env.error(right.where, "invalid.type.expr");  | 
 | 
                }  | 
 | 
 | 
 | 
                // We used to check that 'right.type' is accessible here,  | 
 | 
                // per JLS 6.6.1.  As a result of the fix for 4102393, however,  | 
 | 
                // the qualifying class name must exactly match an enclosing  | 
 | 
                // outer class, which is necessarily accessible.  | 
 | 
 | 
 | 
                  | 
 | 
                if (ctx.field.isSynthetic())  | 
 | 
                    throw new CompilerError("synthetic qualified this"); | 
 | 
                /*********************************/  | 
 | 
 | 
 | 
                // A.this means we're inside an A and we want its self ptr.  | 
 | 
                // C.this is always the same as this when C is innermost.  | 
 | 
                // Another A.this means we skip out to get a "hidden" this,  | 
 | 
                // just as ASuper.foo skips out to get a hidden variable.  | 
 | 
                // Last argument 'true' means we want an exact class match,  | 
 | 
                  | 
 | 
                implementation = ctx.findOuterLink(env, where, clazz, null, true);  | 
 | 
                vset = implementation.checkValue(env, ctx, vset, exp);  | 
 | 
                if (id == idSuper) { | 
 | 
                    type = clazz.getSuperClass().getType();  | 
 | 
                } else { | 
 | 
                    type = clazz.getType();  | 
 | 
                }  | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            field = clazz.getVariable(env, id, sourceClass);  | 
 | 
 | 
 | 
            if (field == null && staticRef && loc != null) { | 
 | 
                // Is this field expression an inner type?  | 
 | 
                // Search the class and its supers (but not its outers).  | 
 | 
                // QUERY: We may need to get the inner class from a  | 
 | 
                // superclass of 'clazz'.  This call is prepared to  | 
 | 
                // resolve the superclass if necessary.  Can we arrange  | 
 | 
                // to assure that it is always previously resolved?  | 
 | 
                // This is one of a small number of problematic calls that  | 
 | 
                // requires 'getSuperClass' to resolve superclasses on demand.  | 
 | 
                  | 
 | 
                field = clazz.getInnerClass(env, id);  | 
 | 
                if (field != null) { | 
 | 
                    return checkInnerClass(env, ctx, vset, exp, loc);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            // If not a variable reference, diagnose error if name is  | 
 | 
            // that of a method.  | 
 | 
 | 
 | 
            if (field == null) { | 
 | 
                if ((field = clazz.findAnyMethod(env, id)) != null) { | 
 | 
                    env.error(where, "invalid.field",  | 
 | 
                              id, field.getClassDeclaration());  | 
 | 
                } else { | 
 | 
                    env.error(where, "no.such.field", id, clazz);  | 
 | 
                }  | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
            // At this point, we have identified a valid field.  | 
 | 
 | 
 | 
              | 
 | 
            if (!FieldExpression.isTypeAccessible(where, env, right.type, sourceClass)) { | 
 | 
                ClassDeclaration cdecl = sourceClass.getClassDeclaration();  | 
 | 
                if (staticRef) { | 
 | 
                    env.error(where, "no.type.access",  | 
 | 
                              id, right.type.toString(), cdecl);  | 
 | 
                } else { | 
 | 
                    env.error(where, "cant.access.member.type",  | 
 | 
                              id, right.type.toString(), cdecl);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            type = field.getType();  | 
 | 
 | 
 | 
            if (!sourceClass.canAccess(env, field)) { | 
 | 
                env.error(where, "no.field.access",  | 
 | 
                          id, clazz, sourceClass.getClassDeclaration());  | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (staticRef && !field.isStatic()) { | 
 | 
                // 'Class.field' is not legal when field is not static;  | 
 | 
                // see JLS 15.13.1.  This case was permitted by javac  | 
 | 
                // prior to 1.2; static refs were silently changed to  | 
 | 
                  | 
 | 
                env.error(where, "no.static.field.access", id, clazz);  | 
 | 
                return vset;  | 
 | 
            } else { | 
 | 
                  | 
 | 
                implementation = implementFieldAccess(env, ctx, right, isLHS);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            if (field.isProtected()  | 
 | 
                && !(right instanceof SuperExpression  | 
 | 
                       | 
 | 
                     || (right instanceof FieldExpression &&  | 
 | 
                         ((FieldExpression)right).id == idSuper))  | 
 | 
                && !sourceClass.protectedAccess(env, field, right.type)) { | 
 | 
                env.error(where, "invalid.protected.field.use",  | 
 | 
                          field.getName(), field.getClassDeclaration(),  | 
 | 
                          right.type);  | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
            if ((!field.isStatic()) &&  | 
 | 
                (right.op == THIS) && !vset.testVar(ctx.getThisNumber())) { | 
 | 
                env.error(where, "access.inst.before.super", id);  | 
 | 
            }  | 
 | 
 | 
 | 
            if (field.reportDeprecated(env)) { | 
 | 
                env.error(where, "warn."+"field.is.deprecated",  | 
 | 
                          id, field.getClassDefinition());  | 
 | 
            }  | 
 | 
 | 
 | 
            // When a package-private class defines public or protected  | 
 | 
            // members, those members may sometimes be accessed from  | 
 | 
            // outside of the package in public subclasses.  In these  | 
 | 
            // cases, we need to massage the getField to refer to  | 
 | 
            // to an accessible subclass rather than the package-private  | 
 | 
            // parent class.  Part of fix for 4135692.  | 
 | 
 | 
 | 
            // Find out if the class which contains this field  | 
 | 
            // reference has access to the class which declares the  | 
 | 
              | 
 | 
            if (sourceClass == ctxClass) { | 
 | 
                ClassDefinition declarer = field.getClassDefinition();  | 
 | 
                if (declarer.isPackagePrivate() &&  | 
 | 
                    !declarer.getName().getQualifier()  | 
 | 
                    .equals(sourceClass.getName().getQualifier())) { | 
 | 
 | 
 | 
                    //System.out.println("The access of member " + | 
 | 
                    //             field + " declared in class " +  | 
 | 
                    //             declarer +  | 
 | 
                    //             " is not allowed by the VM from class  " +  | 
 | 
                    //             ctxClass +  | 
 | 
                    //             ".  Replacing with an access of class " +  | 
 | 
                    //             clazz);  | 
 | 
 | 
 | 
                    // We cannot make this access at the VM level.  | 
 | 
                    // Construct a member which will stand for this  | 
 | 
                      | 
 | 
                    field =  | 
 | 
                        MemberDefinition.makeProxyMember(field, clazz, env);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            sourceClass.addDependency(field.getClassDeclaration());  | 
 | 
 | 
 | 
        } catch (ClassNotFound e) { | 
 | 
            env.error(where, "class.not.found", e.name, ctx.field);  | 
 | 
 | 
 | 
        } catch (AmbiguousMember e) { | 
 | 
            env.error(where, "ambig.field",  | 
 | 
                      id, e.field1.getClassDeclaration(), e.field2.getClassDeclaration());  | 
 | 
        }  | 
 | 
        return vset;  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Return a <code>FieldUpdater</code> object to be used in updating the  | 
 | 
     * value of the location denoted by <code>this</code>, which must be an  | 
 | 
     * expression suitable for the left-hand side of an assignment.  | 
 | 
     * This is used for implementing assignments to private fields for which  | 
 | 
     * an access method is required.  Returns null if no access method is  | 
 | 
     * needed, in which case the assignment is handled in the usual way, by  | 
 | 
     * direct access.  Only simple assignment expressions are handled here  | 
 | 
     * Assignment operators and pre/post increment/decrement operators are  | 
 | 
     * are handled by 'getUpdater' below.  | 
 | 
     * <p>  | 
 | 
     * Must be called after 'checkValue', else 'right' will be invalid.  | 
 | 
     */  | 
 | 
 | 
 | 
 | 
 | 
    public FieldUpdater getAssigner(Environment env, Context ctx) { | 
 | 
        if (field == null) { | 
 | 
            // Field can legitimately be null if the field name was  | 
 | 
            // undefined, in which case an error was reported, but  | 
 | 
            // no value for 'field' is available.  | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
        ClassDefinition abase = accessBase(env, ctx);  | 
 | 
        if (abase != null) { | 
 | 
            MemberDefinition setter = abase.getUpdateMember(env, ctx, field, isQualSuper());  | 
 | 
              | 
 | 
            Expression base = (right == null) ? null : right.copyInline(ctx);  | 
 | 
              | 
 | 
            return new FieldUpdater(where, field, base, null, setter);  | 
 | 
        }  | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * Return a <code>FieldUpdater</code> object to be used in updating the  | 
 | 
     * value of the location denoted by <code>this</code>, which must be an  | 
 | 
     * expression suitable for the left-hand side of an assignment.  This is  | 
 | 
     * used for implementing the assignment operators and the increment and  | 
 | 
     * decrement operators on private fields that are accessed from another  | 
 | 
     * class, e.g, uplevel from an inner class. Returns null if no access  | 
 | 
     * method is needed.  | 
 | 
     * <p>  | 
 | 
     * Must be called after 'checkValue', else 'right' will be invalid.  | 
 | 
     */  | 
 | 
 | 
 | 
    public FieldUpdater getUpdater(Environment env, Context ctx) { | 
 | 
        if (field == null) { | 
 | 
            // Field can legitimately be null if the field name was  | 
 | 
            // undefined, in which case an error was reported, but  | 
 | 
            // no value for 'field' is available.  | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
        ClassDefinition abase = accessBase(env, ctx);  | 
 | 
        if (abase != null) { | 
 | 
            MemberDefinition getter = abase.getAccessMember(env, ctx, field, isQualSuper());  | 
 | 
            MemberDefinition setter = abase.getUpdateMember(env, ctx, field, isQualSuper());  | 
 | 
              | 
 | 
            Expression base = (right == null) ? null : right.copyInline(ctx);  | 
 | 
            return new FieldUpdater(where, field, base, getter, setter);  | 
 | 
        }  | 
 | 
        return null;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private Vset checkInnerClass(Environment env, Context ctx,  | 
 | 
                                 Vset vset, Hashtable exp,  | 
 | 
                                 UnaryExpression loc) { | 
 | 
        ClassDefinition inner = field.getInnerClass();  | 
 | 
        type = inner.getType();  | 
 | 
 | 
 | 
        if (!inner.isTopLevel()) { | 
 | 
            env.error(where, "inner.static.ref", inner.getName());  | 
 | 
        }  | 
 | 
 | 
 | 
        Expression te = new TypeExpression(where, type);  | 
 | 
 | 
 | 
          | 
 | 
        ClassDefinition ctxClass = ctx.field.getClassDefinition();  | 
 | 
        try { | 
 | 
            if (!ctxClass.canAccess(env, field)) { | 
 | 
                ClassDefinition clazz = env.getClassDefinition(right.type);  | 
 | 
                //env.error(where, "no.type.access",  | 
 | 
                  | 
 | 
                env.error(where, "no.type.access",  | 
 | 
                          id, clazz, ctxClass.getClassDeclaration());  | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (field.isProtected()  | 
 | 
                && !(right instanceof SuperExpression  | 
 | 
                       | 
 | 
                     || (right instanceof FieldExpression &&  | 
 | 
                         ((FieldExpression)right).id == idSuper))  | 
 | 
                && !ctxClass.protectedAccess(env, field, right.type)){ | 
 | 
                env.error(where, "invalid.protected.field.use",  | 
 | 
                          field.getName(), field.getClassDeclaration(),  | 
 | 
                          right.type);  | 
 | 
                return vset;  | 
 | 
            }  | 
 | 
 | 
 | 
            inner.noteUsedBy(ctxClass, where, env);  | 
 | 
 | 
 | 
        } catch (ClassNotFound e) { | 
 | 
            env.error(where, "class.not.found", e.name, ctx.field);  | 
 | 
        }  | 
 | 
 | 
 | 
        ctxClass.addDependency(field.getClassDeclaration());  | 
 | 
        if (loc == null)  | 
 | 
              | 
 | 
            return te.checkValue(env, ctx, vset, exp);  | 
 | 
        loc.right = te;  | 
 | 
        return vset;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public Vset checkLHS(Environment env, Context ctx,  | 
 | 
                         Vset vset, Hashtable exp) { | 
 | 
        boolean hadField = (field != null);  | 
 | 
 | 
 | 
          | 
 | 
        checkCommon(env, ctx, vset, exp, null, true);  | 
 | 
 | 
 | 
        // If 'implementation' is set to a non-null value, then the  | 
 | 
        // field expression does not denote an assignable location,  | 
 | 
          | 
 | 
        if (implementation != null) { | 
 | 
              | 
 | 
            return super.checkLHS(env, ctx, vset, exp);  | 
 | 
        }  | 
 | 
 | 
 | 
        if (field != null && field.isFinal() && !hadField) { | 
 | 
            if (field.isBlankFinal()) { | 
 | 
                if (field.isStatic()) { | 
 | 
                    if (right != null) { | 
 | 
                        env.error(where, "qualified.static.final.assign");  | 
 | 
                    }  | 
 | 
                    // Continue with checking anyhow.  | 
 | 
                    // In fact, it would be easy to allow this case.  | 
 | 
                } else { | 
 | 
                    if ((right != null) && (right.op != THIS)) { | 
 | 
                        env.error(where, "bad.qualified.final.assign", field.getName());  | 
 | 
                        // The actual instance could be anywhere, so don't  | 
 | 
                          | 
 | 
                        return vset;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                vset = checkFinalAssign(env, ctx, vset, where, field);  | 
 | 
            } else { | 
 | 
                env.error(where, "assign.to.final", id);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return vset;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public Vset checkAssignOp(Environment env, Context ctx,  | 
 | 
                              Vset vset, Hashtable exp, Expression outside) { | 
 | 
 | 
 | 
          | 
 | 
        checkCommon(env, ctx, vset, exp, null, true);  | 
 | 
 | 
 | 
        // If 'implementation' is set to a non-null value, then the  | 
 | 
        // field expression does not denote an assignable location,  | 
 | 
          | 
 | 
        if (implementation != null) { | 
 | 
            return super.checkLHS(env, ctx, vset, exp);  | 
 | 
        }  | 
 | 
        if (field != null && field.isFinal()) { | 
 | 
            env.error(where, "assign.to.final", id);  | 
 | 
        }  | 
 | 
        return vset;  | 
 | 
    }  | 
 | 
 | 
 | 
    /**  | 
 | 
     * There is a simple assignment being made to the given final field.  | 
 | 
     * The field was named either by a simple name or by an almost-simple  | 
 | 
     * expression of the form "this.v".  | 
 | 
     * Check if this is a legal assignment.  | 
 | 
     * <p>  | 
 | 
     * Blank final variables can be set in initializers or constructor  | 
 | 
     * bodies.  In all cases there must be definite single assignment.  | 
 | 
     * (All instance and instance variable initializers and each  | 
 | 
     * constructor body are treated as if concatenated for the purposes  | 
 | 
     * of this check.  Assignment to "this.x" is treated as a definite  | 
 | 
     * assignment to the simple name "x" which names the instance variable.)  | 
 | 
     */  | 
 | 
 | 
 | 
    public static Vset checkFinalAssign(Environment env, Context ctx,  | 
 | 
                                        Vset vset, long where,  | 
 | 
                                        MemberDefinition field) { | 
 | 
        if (field.isBlankFinal()  | 
 | 
            && field.getClassDefinition() == ctx.field.getClassDefinition()) { | 
 | 
            int number = ctx.getFieldNumber(field);  | 
 | 
            if (number >= 0 && vset.testVarUnassigned(number)) { | 
 | 
                  | 
 | 
                vset = vset.addVar(number);  | 
 | 
            } else { | 
 | 
                  | 
 | 
                Identifier id = field.getName();  | 
 | 
                env.error(where, "assign.to.blank.final", id);  | 
 | 
            }  | 
 | 
        } else { | 
 | 
              | 
 | 
            Identifier id = field.getName();  | 
 | 
            env.error(where, "assign.to.final", id);  | 
 | 
        }  | 
 | 
        return vset;  | 
 | 
    }  | 
 | 
 | 
 | 
    private static MemberDefinition getClassLiteralCache(Environment env,  | 
 | 
                                                         Context ctx,  | 
 | 
                                                         String className,  | 
 | 
                                                         ClassDefinition c) { | 
 | 
        // Given a class name, look for a static field to cache it.  | 
 | 
        //      className       lname  | 
 | 
        //      pkg.Foo         class$pkg$Foo  | 
 | 
        //      [Lpkg.Foo;      array$Lpkg$Foo  | 
 | 
        //      [[Lpkg.Foo;     array$$Lpkg$Foo  | 
 | 
        //      [I              array$I  | 
 | 
          | 
 | 
        String lname;  | 
 | 
        if (!className.startsWith(SIG_ARRAY)) { | 
 | 
            lname = prefixClass + className.replace('.', '$'); | 
 | 
        } else { | 
 | 
            lname = prefixArray + className.substring(1);  | 
 | 
            lname = lname.replace(SIGC_ARRAY, '$');   | 
 | 
            if (className.endsWith(SIG_ENDCLASS)) { | 
 | 
                  | 
 | 
                lname = lname.substring(0, lname.length() - 1);  | 
 | 
                lname = lname.replace('.', '$'); | 
 | 
            }  | 
 | 
            // else [I => array$I or some such; lname is already OK  | 
 | 
        }  | 
 | 
        Identifier fname = Identifier.lookup(lname);  | 
 | 
 | 
 | 
        // The class to put the cache in is now given as an argument.  | 
 | 
        //  | 
 | 
        // ClassDefinition c = ctx.field.getClassDefinition();  | 
 | 
        // while (c.isInnerClass()) { | 
 | 
        //     c = c.getOuterClass();  | 
 | 
 | 
 | 
        MemberDefinition cfld;  | 
 | 
        try { | 
 | 
            cfld = c.getVariable(env, fname, c);  | 
 | 
        } catch (ClassNotFound ee) { | 
 | 
            return null;  | 
 | 
        } catch (AmbiguousMember ee) { | 
 | 
            return null;  | 
 | 
        }  | 
 | 
 | 
 | 
        // Ignore inherited field.  Each top-level class  | 
 | 
        // containing a given class literal must have its own copy,  | 
 | 
        // both for reasons of binary compatibility and to prevent  | 
 | 
        // access violations should the superclass be in another  | 
 | 
          | 
 | 
        if (cfld != null && cfld.getClassDefinition() == c) { | 
 | 
            return cfld;  | 
 | 
        }  | 
 | 
 | 
 | 
        // Since each class now has its own copy, we might as well  | 
 | 
        // tighten up the access to private (previously default).  | 
 | 
        // Part of fix for 4106051.  | 
 | 
          | 
 | 
        return env.makeMemberDefinition(env, c.getWhere(),  | 
 | 
                                        c, null,  | 
 | 
                                        M_STATIC | M_SYNTHETIC,   | 
 | 
                                        Type.tClassDesc, fname,  | 
 | 
                                        null, null, null);  | 
 | 
    }  | 
 | 
 | 
 | 
    private Expression makeClassLiteralCacheRef(Environment env, Context ctx,  | 
 | 
                                                MemberDefinition lookup,  | 
 | 
                                                MemberDefinition cfld,  | 
 | 
                                                String className) { | 
 | 
        Expression ccls = new TypeExpression(where,  | 
 | 
                                             cfld.getClassDefinition()  | 
 | 
                                             .getType());  | 
 | 
        Expression cache = new FieldExpression(where, ccls, cfld);  | 
 | 
        Expression cacheOK =  | 
 | 
            new NotEqualExpression(where, cache.copyInline(ctx),  | 
 | 
                                   new NullExpression(where));  | 
 | 
        Expression lcls =  | 
 | 
            new TypeExpression(where, lookup.getClassDefinition() .getType());  | 
 | 
        Expression name = new StringExpression(where, className);  | 
 | 
        Expression namearg[] = { name }; | 
 | 
        Expression setCache = new MethodExpression(where, lcls,  | 
 | 
                                                   lookup, namearg);  | 
 | 
        setCache = new AssignExpression(where, cache.copyInline(ctx),  | 
 | 
                                        setCache);  | 
 | 
        return new ConditionalExpression(where, cacheOK, cache, setCache);  | 
 | 
    }  | 
 | 
 | 
 | 
    private Expression makeClassLiteralInlineRef(Environment env, Context ctx,  | 
 | 
                                                 MemberDefinition lookup,  | 
 | 
                                                 String className) { | 
 | 
        Expression lcls =  | 
 | 
            new TypeExpression(where, lookup.getClassDefinition().getType());  | 
 | 
        Expression name = new StringExpression(where, className);  | 
 | 
        Expression namearg[] = { name }; | 
 | 
        Expression getClass = new MethodExpression(where, lcls,  | 
 | 
                                                   lookup, namearg);  | 
 | 
        return getClass;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public boolean isConstant() { | 
 | 
        if (implementation != null)  | 
 | 
            return implementation.isConstant();  | 
 | 
        if ((field != null)  | 
 | 
            && (right == null || right instanceof TypeExpression  | 
 | 
                || (right.op == THIS && right.where == where))) { | 
 | 
            return field.isConstant();  | 
 | 
        }  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public Expression inline(Environment env, Context ctx) { | 
 | 
        if (implementation != null)  | 
 | 
            return implementation.inline(env, ctx);  | 
 | 
        // A field expression may have the side effect of causing  | 
 | 
        // a NullPointerException, so evaluate it even though  | 
 | 
        // the value is not needed.  Similarly, static field dereferences  | 
 | 
        // may cause class initialization, so they mustn't be omitted  | 
 | 
        // either.  | 
 | 
        //  | 
 | 
        // However, NullPointerException can't happen and initialization must  | 
 | 
        // already have occurred if you are dotting into 'this'.  So  | 
 | 
          | 
 | 
        Expression e = inlineValue(env, ctx);  | 
 | 
        if (e instanceof FieldExpression) { | 
 | 
            FieldExpression fe = (FieldExpression) e;  | 
 | 
            if ((fe.right != null) && (fe.right.op==THIS))  | 
 | 
                return null;  | 
 | 
            // It should be possible to split this into two checks: one using  | 
 | 
            // isNonNull() for non-statics and a different check for statics.  | 
 | 
            // That would make the inlining slightly less conservative by  | 
 | 
            // allowing, for example, dotting into String constants.  | 
 | 
            }  | 
 | 
        return e;  | 
 | 
    }  | 
 | 
    public Expression inlineValue(Environment env, Context ctx) { | 
 | 
        if (implementation != null)  | 
 | 
            return implementation.inlineValue(env, ctx);  | 
 | 
        try { | 
 | 
            if (field == null) { | 
 | 
                return this;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (field.isFinal()) { | 
 | 
                Expression e = (Expression)field.getValue(env);  | 
 | 
                if ((e != null) && e.isConstant()) { | 
 | 
                      | 
 | 
                    e = e.copyInline(ctx);  | 
 | 
                    e.where = where;  | 
 | 
                    return new CommaExpression(where, right, e).inlineValue(env, ctx);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            if (right != null) { | 
 | 
                if (field.isStatic()) { | 
 | 
                    Expression e = right.inline(env, ctx);  | 
 | 
                    right = null;  | 
 | 
                    if (e != null) { | 
 | 
                        return new CommaExpression(where, e, this);  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    right = right.inlineValue(env, ctx);  | 
 | 
                }  | 
 | 
            }  | 
 | 
            return this;  | 
 | 
 | 
 | 
        } catch (ClassNotFound e) { | 
 | 
            throw new CompilerError(e);  | 
 | 
        }  | 
 | 
    }  | 
 | 
    public Expression inlineLHS(Environment env, Context ctx) { | 
 | 
        if (implementation != null)  | 
 | 
            return implementation.inlineLHS(env, ctx);  | 
 | 
        if (right != null) { | 
 | 
            if (field.isStatic()) { | 
 | 
                Expression e = right.inline(env, ctx);  | 
 | 
                right = null;  | 
 | 
                if (e != null) { | 
 | 
                    return new CommaExpression(where, e, this);  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                right = right.inlineValue(env, ctx);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return this;  | 
 | 
    }  | 
 | 
 | 
 | 
    public Expression copyInline(Context ctx) { | 
 | 
        if (implementation != null)  | 
 | 
            return implementation.copyInline(ctx);  | 
 | 
        return super.copyInline(ctx);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public int costInline(int thresh, Environment env, Context ctx) { | 
 | 
        if (implementation != null)  | 
 | 
            return implementation.costInline(thresh, env, ctx);  | 
 | 
        if (ctx == null) { | 
 | 
            return 3 + ((right == null) ? 0  | 
 | 
                                        : right.costInline(thresh, env, ctx));  | 
 | 
        }  | 
 | 
          | 
 | 
        ClassDefinition ctxClass = ctx.field.getClassDefinition();  | 
 | 
        try { | 
 | 
            // We only allow the inlining if the current class can access  | 
 | 
              | 
 | 
            if (    ctxClass.permitInlinedAccess(env, field.getClassDeclaration())  | 
 | 
                 && ctxClass.permitInlinedAccess(env, field)) { | 
 | 
                if (right == null) { | 
 | 
                    return 3;  | 
 | 
                } else { | 
 | 
                    ClassDeclaration rt = env.getClassDeclaration(right.type);  | 
 | 
                    if (ctxClass.permitInlinedAccess(env, rt)) { | 
 | 
                        return 3 + right.costInline(thresh, env, ctx);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        } catch (ClassNotFound e) { | 
 | 
        }  | 
 | 
        return thresh;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    int codeLValue(Environment env, Context ctx, Assembler asm) { | 
 | 
        if (implementation != null)  | 
 | 
            throw new CompilerError("codeLValue"); | 
 | 
        if (field.isStatic()) { | 
 | 
            if (right != null) { | 
 | 
                right.code(env, ctx, asm);  | 
 | 
                return 1;  | 
 | 
            }  | 
 | 
            return 0;  | 
 | 
        }  | 
 | 
        right.codeValue(env, ctx, asm);  | 
 | 
        return 1;  | 
 | 
    }  | 
 | 
    void codeLoad(Environment env, Context ctx, Assembler asm) { | 
 | 
        if (field == null) { | 
 | 
            throw new CompilerError("should not be null"); | 
 | 
        }  | 
 | 
        if (field.isStatic()) { | 
 | 
            asm.add(where, opc_getstatic, field);  | 
 | 
        } else { | 
 | 
            asm.add(where, opc_getfield, field);  | 
 | 
        }  | 
 | 
    }  | 
 | 
    void codeStore(Environment env, Context ctx, Assembler asm) { | 
 | 
        if (field.isStatic()) { | 
 | 
            asm.add(where, opc_putstatic, field);  | 
 | 
        } else { | 
 | 
            asm.add(where, opc_putfield, field);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public void codeValue(Environment env, Context ctx, Assembler asm) { | 
 | 
        codeLValue(env, ctx, asm);  | 
 | 
        codeLoad(env, ctx, asm);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    public void print(PrintStream out) { | 
 | 
        out.print("("); | 
 | 
        if (right != null) { | 
 | 
            right.print(out);  | 
 | 
        } else { | 
 | 
            out.print("<empty>"); | 
 | 
        }  | 
 | 
        out.print("." + id + ")"); | 
 | 
        if (implementation != null) { | 
 | 
            out.print("/IMPL="); | 
 | 
            implementation.print(out);  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |