/*
 * 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.
/** * WARNING: The contents of this source file are not part of any * supported API. Code that depends on them does so at its own risk: * they are subject to change or removal without notice. */
public class 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
/** * 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'.
/** * 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
/** * 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
/** * 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.
/** * 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
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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
/** * 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 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); } // Does the qualifier have a meaning of its own? vset = right.checkAmbigName(env, ctx, vset, exp, this); if (right.type == Type.tPackage) { // Are we out of options? if (loc == null) { FieldExpression.reportFailedPackagePrefix(env, right); return vset; } // ASSERT(loc.right == this) // Nope. Is this field expression a type? Identifier nm = toIdentifier(this); if ((nm != null) && env.classExists(nm)) { loc.right = new TypeExpression(where, Type.tClass(nm)); // Check access. (Cf. IdentifierExpression.toResolvedType.) ClassDefinition ctxClass = ctx.field.getClassDefinition(); env.resolve(where, ctxClass, loc.right.type); return vset; } // Let the caller make sense of it, then. 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. // Required by JLS 6.6.1. Fixes 4094658. 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. //ClassDefinition clazz = env.getClassDefinition(right.type); 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. /*** Temporary assertion check ***/ 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, // not a subclass of the specified class ('clazz'). 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 should be an instance variable or class variable. 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. // See 'ClassDefinition.getInnerClass(env, nm)'. 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. // Required by JLS 6.6.1. Fixes 4094658. 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 // be dynamic access of the form 'this.field'. env.error(where, "no.static.field.access", id, clazz); return vset; } else { // Rewrite access to use an access method if necessary. implementation = implementFieldAccess(env, ctx, right, isLHS); } // Check for invalid access to protected field. if (field.isProtected() && !(right instanceof SuperExpression // Extension of JLS 6.6.2 for qualified 'super'. || (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 // public or protected field. 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 in ctxClass and set `field' to refer to it. 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 FieldUpdater object to be used in updating the value of the location denoted by this, 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.

Must be called after 'checkValue', else 'right' will be invalid.

/** * 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. // throw new CompilerError("getAssigner"); return null; } ClassDefinition abase = accessBase(env, ctx); if (abase != null) { MemberDefinition setter = abase.getUpdateMember(env, ctx, field, isQualSuper()); // It may not be necessary to copy 'right' here. Expression base = (right == null) ? null : right.copyInline(ctx); // Created 'FieldUpdater' has no getter method. return new FieldUpdater(where, field, base, null, setter); } return null; }
Return a FieldUpdater object to be used in updating the value of the location denoted by this, 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.

Must be called after 'checkValue', else 'right' will be invalid.

/** * 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. // throw new CompilerError("getUpdater"); 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()); // It may not be necessary to copy 'right' here. Expression base = (right == null) ? null : right.copyInline(ctx); return new FieldUpdater(where, field, base, getter, setter); } return null; }
This field expression is an inner class reference. Finish checking it.
/** * This field expression is an inner class reference. * Finish checking it. */
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); // check access ClassDefinition ctxClass = ctx.field.getClassDefinition(); try { if (!ctxClass.canAccess(env, field)) { ClassDefinition clazz = env.getClassDefinition(right.type); //env.error(where, "no.type.access", // id, clazz, ctx.field.getClassDeclaration()); env.error(where, "no.type.access", id, clazz, ctxClass.getClassDeclaration()); return vset; } if (field.isProtected() && !(right instanceof SuperExpression // Extension of JLS 6.6.2 for qualified 'super'. || (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) // Complain about a free-floating type name. return te.checkValue(env, ctx, vset, exp); loc.right = te; return vset; }
Check the expression if it appears on the LHS of an assignment
/** * Check the expression if it appears on the LHS of an assignment */
public Vset checkLHS(Environment env, Context ctx, Vset vset, Hashtable exp) { boolean hadField = (field != null); //checkValue(env, ctx, vset, exp); 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, // e.g., the 'length' field of an array. if (implementation != null) { // This just reports an error and recovers. 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 // continue with checking the definite assignment status. return vset; } } vset = checkFinalAssign(env, ctx, vset, where, field); } else { env.error(where, "assign.to.final", id); } } return vset; }
Check the expression if it appears on the LHS of an op= expression
/** * Check the expression if it appears on the LHS of an op= expression */
public Vset checkAssignOp(Environment env, Context ctx, Vset vset, Hashtable exp, Expression outside) { //checkValue(env, ctx, vset, exp); 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, // e.g., the 'length' field of an array. 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.

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.)

/** * 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)) { // definite single assignment vset = vset.addVar(number); } else { // it is a blank final in this class, but not assignable Identifier id = field.getName(); env.error(where, "assign.to.blank.final", id); } } else { // give the generic error message 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 // [[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, '$'); // [[[I => array$$$I if (className.endsWith(SIG_ENDCLASS)) { // [Lpkg.Foo; => array$Lpkg$Foo 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 // package. Part of fix 4106051. 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. // ** Temporarily retract this, as it tickles 4098316. return env.makeMemberDefinition(env, c.getWhere(), c, null, M_STATIC | M_SYNTHETIC, // M_PRIVATE, 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; }
Check if constant: Will it inline away?
/** * Check if constant: Will it inline away? */
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; }
Inline
/** * Inline */
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 occured if you are dotting into 'this'. So // allow fields of 'this' to be eliminated as a special case. 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()) { // remove bogus line-number info 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); }
The cost of inlining this expression
/** * The cost of inlining this expression */
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)); } // ctxClass is the current class trying to inline this method ClassDefinition ctxClass = ctx.field.getClassDefinition(); try { // We only allow the inlining if the current class can access // the field, the field's class, and right's declared type. 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; }
Code
/** * Code */
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); }
Print
/** * Print */
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); } } }