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 <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 <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. */ 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 */ 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 */ 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. * <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; } Lines 501–1000 of 1282. Previous lines Load more
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 <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 <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. */ 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 */ 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 */ 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. * <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; }