0001: /*
0002: * Copyright 1994-2003 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.tools.tree;
0027:
0028: import sun.tools.java.*;
0029: import sun.tools.asm.*;
0030: import java.io.PrintStream;
0031: import java.util.Hashtable;
0032:
0033: /**
0034: * WARNING: The contents of this source file are not part of any
0035: * supported API. Code that depends on them does so at its own risk:
0036: * they are subject to change or removal without notice.
0037: */
0038: public class FieldExpression extends UnaryExpression {
0039: Identifier id;
0040: MemberDefinition field;
0041: Expression implementation;
0042:
0043: // The class from which the field is select ed.
0044: ClassDefinition clazz;
0045:
0046: // For an expression of the form '<class>.super', then
0047: // this is <class>, else null.
0048: private ClassDefinition super Base;
0049:
0050: /**
0051: * constructor
0052: */
0053: public FieldExpression(long where, Expression right, Identifier id) {
0054: super (FIELD, where, Type.tError, right);
0055: this .id = id;
0056: }
0057:
0058: public FieldExpression(long where, Expression right,
0059: MemberDefinition field) {
0060: super (FIELD, where, field.getType(), right);
0061: this .id = field.getName();
0062: this .field = field;
0063: }
0064:
0065: public Expression getImplementation() {
0066: if (implementation != null)
0067: return implementation;
0068: return this ;
0069: }
0070:
0071: /**
0072: * Return true if the field is being selected from
0073: * a qualified 'super'.
0074: */
0075: private boolean isQualSuper() {
0076: return super Base != null;
0077: }
0078:
0079: /**
0080: * Convert an '.' expression to a qualified identifier
0081: */
0082: static public Identifier toIdentifier(Expression e) {
0083: StringBuffer buf = new StringBuffer();
0084: while (e.op == FIELD) {
0085: FieldExpression fe = (FieldExpression) e;
0086: if (fe.id == idThis || fe.id == idClass) {
0087: return null;
0088: }
0089: buf.insert(0, fe.id);
0090: buf.insert(0, '.');
0091: e = fe.right;
0092: }
0093: if (e.op != IDENT) {
0094: return null;
0095: }
0096: buf.insert(0, ((IdentifierExpression) e).id);
0097: return Identifier.lookup(buf.toString());
0098: }
0099:
0100: /**
0101: * Convert a qualified name into a type.
0102: * Performs a careful check of each inner-class component,
0103: * including the JLS 6.6.1 access checks that were omitted
0104: * in 'FieldExpression.toType'.
0105: * <p>
0106: * This code is similar to 'checkCommon', which could be cleaned
0107: * up a bit long the lines we have done here.
0108: */
0109: /*-------------------------------------------------------*
0110: Type toQualifiedType(Environment env, Context ctx) {
0111: ClassDefinition ctxClass = ctx.field.getClassDefinition();
0112: Type rty = right.toQualifiedType(env, ctx);
0113: if (rty == Type.tPackage) {
0114: // Is this field expression a non-inner type?
0115: Identifier nm = toIdentifier(this);
0116: if ((nm != null) && env.classExists(nm)) {
0117: Type t = Type.tClass(nm);
0118: if (env.resolve(where, ctxClass, t)) {
0119: return t;
0120: } else {
0121: return null;
0122: }
0123: }
0124: // Not a type. Must be a package prefix.
0125: return Type.tPackage;
0126: }
0127: if (rty == null) {
0128: // An error was already reported, so quit.
0129: return null;
0130: }
0131:
0132: // Check inner-class qualification while unwinding from recursion.
0133: try {
0134: ClassDefinition rightClass = env.getClassDefinition(rty);
0135:
0136: // Local variables, which cannot be inner classes,
0137: // are ignored here, and thus will not hide inner
0138: // classes. Is this correct?
0139: MemberDefinition field = rightClass.getInnerClass(env, id);
0140: if (field == null) {
0141: env.error(where, "inner.class.expected", id, rightClass);
0142: return Type.tError;
0143: }
0144:
0145: ClassDefinition innerClass = field.getInnerClass();
0146: Type t = innerClass.getType();
0147:
0148: if (!ctxClass.canAccess(env, field)) {
0149: env.error(where, "no.type.access", id, rightClass, ctxClass);
0150: return t;
0151: }
0152: if (field.isProtected()
0153: && !ctxClass.protectedAccess(env, field, rty)) {
0154: env.error(where, "invalid.protected.type.use", id, ctxClass, rty);
0155: return t;
0156: }
0157:
0158: // These were ommitted earlier in calls to 'toType', but I can't
0159: // see any reason for that. I think it was an oversight. See
0160: // 'checkCommon' and 'checkInnerClass'.
0161: innerClass.noteUsedBy(ctxClass, where, env);
0162: ctxClass.addDependency(field.getClassDeclaration());
0163:
0164: return t;
0165:
0166: } catch (ClassNotFound e) {
0167: env.error(where, "class.not.found", e.name, ctx.field);
0168: }
0169:
0170: // Class not found.
0171: return null;
0172: }
0173: *-------------------------------------------------------*/
0174:
0175: /**
0176: * Convert an '.' expression to a type
0177: */
0178:
0179: // This is a rewrite to treat qualified names in a
0180: // context in which a type name is expected in the
0181: // same way that they are handled for an ambiguous
0182: // or expression-expected context in 'checkCommon'
0183: // below. The new code is cleaner and allows better
0184: // localization of errors. Unfortunately, most
0185: // qualified names appearing in types are actually
0186: // handled by 'Environment.resolve'. There isn't
0187: // much point, then, in breaking out 'toType' as a
0188: // special case until the other cases can be cleaned
0189: // up as well. For the time being, we will leave this
0190: // code disabled, thus reducing the testing requirements.
0191: /*-------------------------------------------------------*
0192: Type toType(Environment env, Context ctx) {
0193: Type t = toQualifiedType(env, ctx);
0194: if (t == null) {
0195: return Type.tError;
0196: }
0197: if (t == Type.tPackage) {
0198: FieldExpression.reportFailedPackagePrefix(env, right, true);
0199: return Type.tError;
0200: }
0201: return t;
0202: }
0203: *-------------------------------------------------------*/
0204:
0205: Type toType(Environment env, Context ctx) {
0206: Identifier id = toIdentifier(this );
0207: if (id == null) {
0208: env.error(where, "invalid.type.expr");
0209: return Type.tError;
0210: }
0211: Type t = Type.tClass(ctx.resolveName(env, id));
0212: if (env.resolve(where, ctx.field.getClassDefinition(), t)) {
0213: return t;
0214: }
0215: return Type.tError;
0216: }
0217:
0218: /**
0219: * Check if the present name is part of a scoping prefix.
0220: */
0221:
0222: public Vset checkAmbigName(Environment env, Context ctx, Vset vset,
0223: Hashtable exp, UnaryExpression loc) {
0224: if (id == idThis || id == idClass) {
0225: loc = null; // this cannot be a type or package
0226: }
0227: return checkCommon(env, ctx, vset, exp, loc, false);
0228: }
0229:
0230: /**
0231: * Check the expression
0232: */
0233:
0234: public Vset checkValue(Environment env, Context ctx, Vset vset,
0235: Hashtable exp) {
0236: vset = checkCommon(env, ctx, vset, exp, null, false);
0237: if (id == idSuper && type != Type.tError) {
0238: // "super" is not allowed in this context.
0239: // It must always qualify another name.
0240: env.error(where, "undef.var.super", idSuper);
0241: }
0242: return vset;
0243: }
0244:
0245: /**
0246: * If 'checkAmbiguousName' returns 'Package.tPackage', then it was
0247: * unable to resolve any prefix of the qualified name. This method
0248: * attempts to diagnose the problem.
0249: */
0250:
0251: static void reportFailedPackagePrefix(Environment env,
0252: Expression right) {
0253: reportFailedPackagePrefix(env, right, false);
0254: }
0255:
0256: static void reportFailedPackagePrefix(Environment env,
0257: Expression right, boolean mustBeType) {
0258: // Find the leftmost component, and put the blame on it.
0259: Expression idp = right;
0260: while (idp instanceof UnaryExpression)
0261: idp = ((UnaryExpression) idp).right;
0262: IdentifierExpression ie = (IdentifierExpression) idp;
0263:
0264: // It may be that 'ie' refers to an ambiguous class. Check this
0265: // with a call to env.resolve(). Part of solution for 4059855.
0266: try {
0267: env.resolve(ie.id);
0268: } catch (AmbiguousClass e) {
0269: env.error(right.where, "ambig.class", e.name1, e.name2);
0270: return;
0271: } catch (ClassNotFound e) {
0272: }
0273:
0274: if (idp == right) {
0275: if (mustBeType) {
0276: env.error(ie.where, "undef.class", ie.id);
0277: } else {
0278: env.error(ie.where, "undef.var.or.class", ie.id);
0279: }
0280: } else {
0281: if (mustBeType) {
0282: env.error(ie.where, "undef.class.or.package", ie.id);
0283: } else {
0284: env
0285: .error(ie.where, "undef.var.class.or.package",
0286: ie.id);
0287: }
0288: }
0289: }
0290:
0291: /**
0292: * Rewrite accesses to private fields of another class.
0293: */
0294:
0295: private Expression implementFieldAccess(Environment env,
0296: Context ctx, Expression base, boolean isLHS) {
0297: ClassDefinition abase = accessBase(env, ctx);
0298: if (abase != null) {
0299:
0300: // If the field is final and its initializer is a constant expression,
0301: // then just rewrite to the constant expression. This is not just an
0302: // optimization, but is required for correctness. If an expression is
0303: // rewritten to use an access method, then its status as a constant
0304: // expression is lost. This was the cause of bug 4098737. Note that
0305: // a call to 'getValue(env)' below would not be correct, as it attempts
0306: // to simplify the initial value expression, which must not occur until
0307: // after the checking phase, for example, after definite assignment checks.
0308: if (field.isFinal()) {
0309: Expression e = (Expression) field.getValue();
0310: // Must not be LHS here. Test as a precaution,
0311: // as we may not be careful to avoid this when
0312: // compiling an erroneous program.
0313: if ((e != null) && e.isConstant() && !isLHS) {
0314: return e.copyInline(ctx);
0315: }
0316: }
0317:
0318: //System.out.println("Finding access method for " + field);
0319: MemberDefinition af = abase.getAccessMember(env, ctx,
0320: field, isQualSuper());
0321: //System.out.println("Using access method " + af);
0322:
0323: if (!isLHS) {
0324: //System.out.println("Reading " + field +
0325: // " via access method " + af);
0326: // If referencing the value of the field, then replace
0327: // with a call to the access method. If assigning to
0328: // the field, a call to the update method will be
0329: // generated later. It is important that
0330: // 'implementation' not be set to non-null if the
0331: // expression is a valid assignment target.
0332: // (See 'checkLHS'.)
0333: if (field.isStatic()) {
0334: Expression args[] = {};
0335: Expression call = new MethodExpression(where, null,
0336: af, args);
0337: return new CommaExpression(where, base, call);
0338: } else {
0339: Expression args[] = { base };
0340: return new MethodExpression(where, null, af, args);
0341: }
0342: }
0343: }
0344:
0345: return null;
0346: }
0347:
0348: /**
0349: * Determine if an access method is required, and, if so, return
0350: * the class in which it should appear, else return null.
0351: */
0352: private ClassDefinition accessBase(Environment env, Context ctx) {
0353: if (field.isPrivate()) {
0354: ClassDefinition cdef = field.getClassDefinition();
0355: ClassDefinition ctxClass = ctx.field.getClassDefinition();
0356: if (cdef == ctxClass) {
0357: // If access from same class as field, then no access
0358: // method is needed.
0359: return null;
0360: }
0361: // An access method is needed in the class containing the field.
0362: return cdef;
0363: } else if (field.isProtected()) {
0364: if (super Base == null) {
0365: // If access is not via qualified super, then it is either
0366: // OK without an access method, or it is an illegal access
0367: // for which an error message should have been issued.
0368: // Legal accesses include unqualified 'super.foo'.
0369: return null;
0370: }
0371: ClassDefinition cdef = field.getClassDefinition();
0372: ClassDefinition ctxClass = ctx.field.getClassDefinition();
0373: if (cdef.inSamePackage(ctxClass)) {
0374: // Access to protected member in same package always allowed.
0375: return null;
0376: }
0377: // Access via qualified super.
0378: // An access method is needed in the qualifying class, an
0379: // immediate subclass of the class containing the selected
0380: // field. NOTE: The fact that the returned class is 'superBase'
0381: // carries the additional bit of information (that a special
0382: // superclass access method is being created) which is provided
0383: // to 'getAccessMember' via its 'isSuper' argument.
0384: return super Base;
0385: } else {
0386: // No access method needed.
0387: return null;
0388: }
0389: }
0390:
0391: /**
0392: * Determine if a type is accessible from a given class.
0393: */
0394: static boolean isTypeAccessible(long where, Environment env,
0395: Type t, ClassDefinition c) {
0396: switch (t.getTypeCode()) {
0397: case TC_CLASS:
0398: try {
0399: Identifier nm = t.getClassName();
0400: // Why not just use 'Environment.getClassDeclaration' here?
0401: // But 'Environment.getClassDeclation' has special treatment
0402: // for local classes that is probably necessary. This code
0403: // was adapted from 'Environment.resolve'.
0404: ClassDefinition def = env.getClassDefinition(t);
0405: return c.canAccess(env, def.getClassDeclaration());
0406: } catch (ClassNotFound e) {
0407: } // Ignore -- reported elsewhere.
0408: return true;
0409: case TC_ARRAY:
0410: return isTypeAccessible(where, env, t.getElementType(), c);
0411: default:
0412: return true;
0413: }
0414: }
0415:
0416: /**
0417: * Common code for checkValue and checkAmbigName
0418: */
0419:
0420: private Vset checkCommon(Environment env, Context ctx, Vset vset,
0421: Hashtable exp, UnaryExpression loc, boolean isLHS) {
0422:
0423: // Handle class literal, e.g., 'x.class'.
0424: if (id == idClass) {
0425:
0426: // In 'x.class', 'x' must be a type name, possibly qualified.
0427: Type t = right.toType(env, ctx);
0428:
0429: if (!t.isType(TC_CLASS) && !t.isType(TC_ARRAY)) {
0430: if (t.isType(TC_ERROR)) {
0431: type = Type.tClassDesc;
0432: return vset;
0433: }
0434: String wrc = null;
0435: switch (t.getTypeCode()) {
0436: case TC_VOID:
0437: wrc = "Void";
0438: break;
0439: case TC_BOOLEAN:
0440: wrc = "Boolean";
0441: break;
0442: case TC_BYTE:
0443: wrc = "Byte";
0444: break;
0445: case TC_CHAR:
0446: wrc = "Character";
0447: break;
0448: case TC_SHORT:
0449: wrc = "Short";
0450: break;
0451: case TC_INT:
0452: wrc = "Integer";
0453: break;
0454: case TC_FLOAT:
0455: wrc = "Float";
0456: break;
0457: case TC_LONG:
0458: wrc = "Long";
0459: break;
0460: case TC_DOUBLE:
0461: wrc = "Double";
0462: break;
0463: default:
0464: env.error(right.where, "invalid.type.expr");
0465: return vset;
0466: }
0467: Identifier wid = Identifier.lookup(idJavaLang + "."
0468: + wrc);
0469: Expression wcls = new TypeExpression(where, Type
0470: .tClass(wid));
0471: implementation = new FieldExpression(where, wcls,
0472: idTYPE);
0473: vset = implementation.checkValue(env, ctx, vset, exp);
0474: type = implementation.type; // java.lang.Class
0475: return vset;
0476: }
0477:
0478: // Check for the bogus type `array of void'
0479: if (t.isVoidArray()) {
0480: type = Type.tClassDesc;
0481: env.error(right.where, "void.array");
0482: return vset;
0483: }
0484:
0485: // it is a class or array
0486: long fwhere = ctx.field.getWhere();
0487: ClassDefinition fcls = ctx.field.getClassDefinition();
0488: MemberDefinition lookup = fcls
0489: .getClassLiteralLookup(fwhere);
0490:
0491: String sig = t.getTypeSignature();
0492: String className;
0493: if (t.isType(TC_CLASS)) {
0494: // sig is like "Lfoo/bar;", name is like "foo.bar".
0495: // We assume SIG_CLASS and SIG_ENDCLASS are 1 char each.
0496: className = sig.substring(1, sig.length() - 1).replace(
0497: SIGC_PACKAGE, '.');
0498: } else {
0499: // sig is like "[Lfoo/bar;" or "[I";
0500: // name is like "[Lfoo.bar" or (again) "[I".
0501: className = sig.replace(SIGC_PACKAGE, '.');
0502: }
0503:
0504: if (fcls.isInterface()) {
0505: // The immediately-enclosing type is an interface.
0506: // The class literal can only appear in an initialization
0507: // expression, so don't bother caching it. (This could
0508: // lose if many initializations use the same class literal,
0509: // but saves time and code space otherwise.)
0510: implementation = makeClassLiteralInlineRef(env, ctx,
0511: lookup, className);
0512: } else {
0513: // Cache the call to the helper, as it may be executed
0514: // many times (e.g., if the class literal is inside a loop).
0515: ClassDefinition inClass = lookup.getClassDefinition();
0516: MemberDefinition cfld = getClassLiteralCache(env, ctx,
0517: className, inClass);
0518: implementation = makeClassLiteralCacheRef(env, ctx,
0519: lookup, cfld, className);
0520: }
0521:
0522: vset = implementation.checkValue(env, ctx, vset, exp);
0523: type = implementation.type; // java.lang.Class
0524: return vset;
0525: }
0526:
0527: // Arrive here if not a class literal.
0528:
0529: if (field != null) {
0530:
0531: // The field as been pre-set, e.g., as the result of transforming
0532: // an 'IdentifierExpression'. Most error-checking has already been
0533: // performed at this point.
0534: // QUERY: Why don't we further unify checking of identifier
0535: // expressions and field expressions that denote instance and
0536: // class variables?
0537:
0538: implementation = implementFieldAccess(env, ctx, right,
0539: isLHS);
0540: return (right == null) ? vset : right.checkAmbigName(env,
0541: ctx, vset, exp, this );
0542: }
0543:
0544: // Does the qualifier have a meaning of its own?
0545: vset = right.checkAmbigName(env, ctx, vset, exp, this );
0546: if (right.type == Type.tPackage) {
0547: // Are we out of options?
0548: if (loc == null) {
0549: FieldExpression.reportFailedPackagePrefix(env, right);
0550: return vset;
0551: }
0552:
0553: // ASSERT(loc.right == this)
0554:
0555: // Nope. Is this field expression a type?
0556: Identifier nm = toIdentifier(this );
0557: if ((nm != null) && env.classExists(nm)) {
0558: loc.right = new TypeExpression(where, Type.tClass(nm));
0559: // Check access. (Cf. IdentifierExpression.toResolvedType.)
0560: ClassDefinition ctxClass = ctx.field
0561: .getClassDefinition();
0562: env.resolve(where, ctxClass, loc.right.type);
0563: return vset;
0564: }
0565:
0566: // Let the caller make sense of it, then.
0567: type = Type.tPackage;
0568: return vset;
0569: }
0570:
0571: // Good; we have a well-defined qualifier type.
0572:
0573: ClassDefinition ctxClass = ctx.field.getClassDefinition();
0574: boolean staticRef = (right instanceof TypeExpression);
0575:
0576: try {
0577:
0578: // Handle array 'length' field, e.g., 'x.length'.
0579:
0580: if (!right.type.isType(TC_CLASS)) {
0581: if (right.type.isType(TC_ARRAY) && id.equals(idLength)) {
0582: // Verify that the type of the base expression is accessible.
0583: // Required by JLS 6.6.1. Fixes 4094658.
0584: if (!FieldExpression.isTypeAccessible(where, env,
0585: right.type, ctxClass)) {
0586: ClassDeclaration cdecl = ctxClass
0587: .getClassDeclaration();
0588: if (staticRef) {
0589: env.error(where, "no.type.access", id,
0590: right.type.toString(), cdecl);
0591: } else {
0592: env.error(where, "cant.access.member.type",
0593: id, right.type.toString(), cdecl);
0594: }
0595: }
0596: type = Type.tInt;
0597: implementation = new LengthExpression(where, right);
0598: return vset;
0599: }
0600: if (!right.type.isType(TC_ERROR)) {
0601: env.error(where, "invalid.field.reference", id,
0602: right.type);
0603: }
0604: return vset;
0605: }
0606:
0607: // At this point, we know that 'right.type' is a class type.
0608:
0609: // Note that '<expr>.super(...)' and '<expr>.this(...)' cases never
0610: // reach here. Instead, '<expr>' is stored as the 'outerArg' field
0611: // of a 'SuperExpression' or 'ThisExpression' node.
0612:
0613: // If our prefix is of the form '<class>.super', then we are
0614: // about to do a field selection '<class>.super.<field>'.
0615: // Save the qualifying class in 'superBase', which is non-null
0616: // only if the current FieldExpression is a qualified 'super' form.
0617: // Also, set 'sourceClass' to the "effective accessing class" relative
0618: // to which access checks will be performed. Normally, this is the
0619: // immediately enclosing class. For '<class>.this' and '<class>.super',
0620: // however, we use <class>.
0621:
0622: ClassDefinition sourceClass = ctxClass;
0623: if (right instanceof FieldExpression) {
0624: Identifier id = ((FieldExpression) right).id;
0625: if (id == idThis) {
0626: sourceClass = ((FieldExpression) right).clazz;
0627: } else if (id == idSuper) {
0628: sourceClass = ((FieldExpression) right).clazz;
0629: super Base = sourceClass;
0630: }
0631: }
0632:
0633: // Handle 'class.this' and 'class.super'.
0634: //
0635: // Suppose 'super.name' appears within a class C with immediate
0636: // superclass S. According to JLS 15.10.2, 'super.name' in this
0637: // case is equivalent to '((S)this).name'. Analogously, we interpret
0638: // 'class.super.name' as '((S)(class.this)).name', where S is the
0639: // immediate superclass of (enclosing) class 'class'.
0640: // Note that 'super' may not stand alone as an expression, but must
0641: // occur as the qualifying expression of a field access or a method
0642: // invocation. This is enforced in 'SuperExpression.checkValue' and
0643: // 'FieldExpression.checkValue', and need not concern us here.
0644:
0645: //ClassDefinition clazz = env.getClassDefinition(right.type);
0646: clazz = env.getClassDefinition(right.type);
0647: if (id == idThis || id == idSuper) {
0648: if (!staticRef) {
0649: env.error(right.where, "invalid.type.expr");
0650: }
0651:
0652: // We used to check that 'right.type' is accessible here,
0653: // per JLS 6.6.1. As a result of the fix for 4102393, however,
0654: // the qualifying class name must exactly match an enclosing
0655: // outer class, which is necessarily accessible.
0656:
0657: /*** Temporary assertion check ***/
0658: if (ctx.field.isSynthetic())
0659: throw new CompilerError("synthetic qualified this");
0660: /*********************************/
0661:
0662: // A.this means we're inside an A and we want its self ptr.
0663: // C.this is always the same as this when C is innermost.
0664: // Another A.this means we skip out to get a "hidden" this,
0665: // just as ASuper.foo skips out to get a hidden variable.
0666: // Last argument 'true' means we want an exact class match,
0667: // not a subclass of the specified class ('clazz').
0668: implementation = ctx.findOuterLink(env, where, clazz,
0669: null, true);
0670: vset = implementation.checkValue(env, ctx, vset, exp);
0671: if (id == idSuper) {
0672: type = clazz.getSuperClass().getType();
0673: } else {
0674: type = clazz.getType();
0675: }
0676: return vset;
0677: }
0678:
0679: // Field should be an instance variable or class variable.
0680: field = clazz.getVariable(env, id, sourceClass);
0681:
0682: if (field == null && staticRef && loc != null) {
0683: // Is this field expression an inner type?
0684: // Search the class and its supers (but not its outers).
0685: // QUERY: We may need to get the inner class from a
0686: // superclass of 'clazz'. This call is prepared to
0687: // resolve the superclass if necessary. Can we arrange
0688: // to assure that it is always previously resolved?
0689: // This is one of a small number of problematic calls that
0690: // requires 'getSuperClass' to resolve superclasses on demand.
0691: // See 'ClassDefinition.getInnerClass(env, nm)'.
0692: field = clazz.getInnerClass(env, id);
0693: if (field != null) {
0694: return checkInnerClass(env, ctx, vset, exp, loc);
0695: }
0696: }
0697:
0698: // If not a variable reference, diagnose error if name is
0699: // that of a method.
0700:
0701: if (field == null) {
0702: if ((field = clazz.findAnyMethod(env, id)) != null) {
0703: env.error(where, "invalid.field", id, field
0704: .getClassDeclaration());
0705: } else {
0706: env.error(where, "no.such.field", id, clazz);
0707: }
0708: return vset;
0709: }
0710:
0711: // At this point, we have identified a valid field.
0712:
0713: // Required by JLS 6.6.1. Fixes 4094658.
0714: if (!FieldExpression.isTypeAccessible(where, env,
0715: right.type, sourceClass)) {
0716: ClassDeclaration cdecl = sourceClass
0717: .getClassDeclaration();
0718: if (staticRef) {
0719: env.error(where, "no.type.access", id, right.type
0720: .toString(), cdecl);
0721: } else {
0722: env.error(where, "cant.access.member.type", id,
0723: right.type.toString(), cdecl);
0724: }
0725: }
0726:
0727: type = field.getType();
0728:
0729: if (!sourceClass.canAccess(env, field)) {
0730: env.error(where, "no.field.access", id, clazz,
0731: sourceClass.getClassDeclaration());
0732: return vset;
0733: }
0734:
0735: if (staticRef && !field.isStatic()) {
0736: // 'Class.field' is not legal when field is not static;
0737: // see JLS 15.13.1. This case was permitted by javac
0738: // prior to 1.2; static refs were silently changed to
0739: // be dynamic access of the form 'this.field'.
0740: env.error(where, "no.static.field.access", id, clazz);
0741: return vset;
0742: } else {
0743: // Rewrite access to use an access method if necessary.
0744: implementation = implementFieldAccess(env, ctx, right,
0745: isLHS);
0746: }
0747:
0748: // Check for invalid access to protected field.
0749: if (field.isProtected()
0750: && !(right instanceof SuperExpression
0751: // Extension of JLS 6.6.2 for qualified 'super'.
0752: || (right instanceof FieldExpression && ((FieldExpression) right).id == idSuper))
0753: && !sourceClass.protectedAccess(env, field,
0754: right.type)) {
0755: env.error(where, "invalid.protected.field.use", field
0756: .getName(), field.getClassDeclaration(),
0757: right.type);
0758: return vset;
0759: }
0760:
0761: if ((!field.isStatic()) && (right.op == THIS)
0762: && !vset.testVar(ctx.getThisNumber())) {
0763: env.error(where, "access.inst.before.super", id);
0764: }
0765:
0766: if (field.reportDeprecated(env)) {
0767: env.error(where, "warn." + "field.is.deprecated", id,
0768: field.getClassDefinition());
0769: }
0770:
0771: // When a package-private class defines public or protected
0772: // members, those members may sometimes be accessed from
0773: // outside of the package in public subclasses. In these
0774: // cases, we need to massage the getField to refer to
0775: // to an accessible subclass rather than the package-private
0776: // parent class. Part of fix for 4135692.
0777:
0778: // Find out if the class which contains this field
0779: // reference has access to the class which declares the
0780: // public or protected field.
0781: if (sourceClass == ctxClass) {
0782: ClassDefinition declarer = field.getClassDefinition();
0783: if (declarer.isPackagePrivate()
0784: && !declarer.getName().getQualifier().equals(
0785: sourceClass.getName().getQualifier())) {
0786:
0787: //System.out.println("The access of member " +
0788: // field + " declared in class " +
0789: // declarer +
0790: // " is not allowed by the VM from class " +
0791: // ctxClass +
0792: // ". Replacing with an access of class " +
0793: // clazz);
0794:
0795: // We cannot make this access at the VM level.
0796: // Construct a member which will stand for this
0797: // field in ctxClass and set `field' to refer to it.
0798: field = MemberDefinition.makeProxyMember(field,
0799: clazz, env);
0800: }
0801: }
0802:
0803: sourceClass.addDependency(field.getClassDeclaration());
0804:
0805: } catch (ClassNotFound e) {
0806: env.error(where, "class.not.found", e.name, ctx.field);
0807:
0808: } catch (AmbiguousMember e) {
0809: env.error(where, "ambig.field", id, e.field1
0810: .getClassDeclaration(), e.field2
0811: .getClassDeclaration());
0812: }
0813: return vset;
0814: }
0815:
0816: /**
0817: * Return a <code>FieldUpdater</code> object to be used in updating the
0818: * value of the location denoted by <code>this</code>, which must be an
0819: * expression suitable for the left-hand side of an assignment.
0820: * This is used for implementing assignments to private fields for which
0821: * an access method is required. Returns null if no access method is
0822: * needed, in which case the assignment is handled in the usual way, by
0823: * direct access. Only simple assignment expressions are handled here
0824: * Assignment operators and pre/post increment/decrement operators are
0825: * are handled by 'getUpdater' below.
0826: * <p>
0827: * Must be called after 'checkValue', else 'right' will be invalid.
0828: */
0829:
0830: public FieldUpdater getAssigner(Environment env, Context ctx) {
0831: if (field == null) {
0832: // Field can legitimately be null if the field name was
0833: // undefined, in which case an error was reported, but
0834: // no value for 'field' is available.
0835: // throw new CompilerError("getAssigner");
0836: return null;
0837: }
0838: ClassDefinition abase = accessBase(env, ctx);
0839: if (abase != null) {
0840: MemberDefinition setter = abase.getUpdateMember(env, ctx,
0841: field, isQualSuper());
0842: // It may not be necessary to copy 'right' here.
0843: Expression base = (right == null) ? null : right
0844: .copyInline(ctx);
0845: // Created 'FieldUpdater' has no getter method.
0846: return new FieldUpdater(where, field, base, null, setter);
0847: }
0848: return null;
0849: }
0850:
0851: /**
0852: * Return a <code>FieldUpdater</code> object to be used in updating the
0853: * value of the location denoted by <code>this</code>, which must be an
0854: * expression suitable for the left-hand side of an assignment. This is
0855: * used for implementing the assignment operators and the increment and
0856: * decrement operators on private fields that are accessed from another
0857: * class, e.g, uplevel from an inner class. Returns null if no access
0858: * method is needed.
0859: * <p>
0860: * Must be called after 'checkValue', else 'right' will be invalid.
0861: */
0862:
0863: public FieldUpdater getUpdater(Environment env, Context ctx) {
0864: if (field == null) {
0865: // Field can legitimately be null if the field name was
0866: // undefined, in which case an error was reported, but
0867: // no value for 'field' is available.
0868: // throw new CompilerError("getUpdater");
0869: return null;
0870: }
0871: ClassDefinition abase = accessBase(env, ctx);
0872: if (abase != null) {
0873: MemberDefinition getter = abase.getAccessMember(env, ctx,
0874: field, isQualSuper());
0875: MemberDefinition setter = abase.getUpdateMember(env, ctx,
0876: field, isQualSuper());
0877: // It may not be necessary to copy 'right' here.
0878: Expression base = (right == null) ? null : right
0879: .copyInline(ctx);
0880: return new FieldUpdater(where, field, base, getter, setter);
0881: }
0882: return null;
0883: }
0884:
0885: /**
0886: * This field expression is an inner class reference.
0887: * Finish checking it.
0888: */
0889: private Vset checkInnerClass(Environment env, Context ctx,
0890: Vset vset, Hashtable exp, UnaryExpression loc) {
0891: ClassDefinition inner = field.getInnerClass();
0892: type = inner.getType();
0893:
0894: if (!inner.isTopLevel()) {
0895: env.error(where, "inner.static.ref", inner.getName());
0896: }
0897:
0898: Expression te = new TypeExpression(where, type);
0899:
0900: // check access
0901: ClassDefinition ctxClass = ctx.field.getClassDefinition();
0902: try {
0903: if (!ctxClass.canAccess(env, field)) {
0904: ClassDefinition clazz = env
0905: .getClassDefinition(right.type);
0906: //env.error(where, "no.type.access",
0907: // id, clazz, ctx.field.getClassDeclaration());
0908: env.error(where, "no.type.access", id, clazz, ctxClass
0909: .getClassDeclaration());
0910: return vset;
0911: }
0912:
0913: if (field.isProtected()
0914: && !(right instanceof SuperExpression
0915: // Extension of JLS 6.6.2 for qualified 'super'.
0916: || (right instanceof FieldExpression && ((FieldExpression) right).id == idSuper))
0917: && !ctxClass
0918: .protectedAccess(env, field, right.type)) {
0919: env.error(where, "invalid.protected.field.use", field
0920: .getName(), field.getClassDeclaration(),
0921: right.type);
0922: return vset;
0923: }
0924:
0925: inner.noteUsedBy(ctxClass, where, env);
0926:
0927: } catch (ClassNotFound e) {
0928: env.error(where, "class.not.found", e.name, ctx.field);
0929: }
0930:
0931: ctxClass.addDependency(field.getClassDeclaration());
0932: if (loc == null)
0933: // Complain about a free-floating type name.
0934: return te.checkValue(env, ctx, vset, exp);
0935: loc.right = te;
0936: return vset;
0937: }
0938:
0939: /**
0940: * Check the expression if it appears on the LHS of an assignment
0941: */
0942: public Vset checkLHS(Environment env, Context ctx, Vset vset,
0943: Hashtable exp) {
0944: boolean hadField = (field != null);
0945:
0946: //checkValue(env, ctx, vset, exp);
0947: checkCommon(env, ctx, vset, exp, null, true);
0948:
0949: // If 'implementation' is set to a non-null value, then the
0950: // field expression does not denote an assignable location,
0951: // e.g., the 'length' field of an array.
0952: if (implementation != null) {
0953: // This just reports an error and recovers.
0954: return super .checkLHS(env, ctx, vset, exp);
0955: }
0956:
0957: if (field != null && field.isFinal() && !hadField) {
0958: if (field.isBlankFinal()) {
0959: if (field.isStatic()) {
0960: if (right != null) {
0961: env.error(where,
0962: "qualified.static.final.assign");
0963: }
0964: // Continue with checking anyhow.
0965: // In fact, it would be easy to allow this case.
0966: } else {
0967: if ((right != null) && (right.op != THIS)) {
0968: env.error(where, "bad.qualified.final.assign",
0969: field.getName());
0970: // The actual instance could be anywhere, so don't
0971: // continue with checking the definite assignment status.
0972: return vset;
0973: }
0974: }
0975: vset = checkFinalAssign(env, ctx, vset, where, field);
0976: } else {
0977: env.error(where, "assign.to.final", id);
0978: }
0979: }
0980: return vset;
0981: }
0982:
0983: /**
0984: * Check the expression if it appears on the LHS of an op= expression
0985: */
0986: public Vset checkAssignOp(Environment env, Context ctx, Vset vset,
0987: Hashtable exp, Expression outside) {
0988:
0989: //checkValue(env, ctx, vset, exp);
0990: checkCommon(env, ctx, vset, exp, null, true);
0991:
0992: // If 'implementation' is set to a non-null value, then the
0993: // field expression does not denote an assignable location,
0994: // e.g., the 'length' field of an array.
0995: if (implementation != null) {
0996: return super .checkLHS(env, ctx, vset, exp);
0997: }
0998: if (field != null && field.isFinal()) {
0999: env.error(where, "assign.to.final", id);
1000: }
1001: return vset;
1002: }
1003:
1004: /**
1005: * There is a simple assignment being made to the given final field.
1006: * The field was named either by a simple name or by an almost-simple
1007: * expression of the form "this.v".
1008: * Check if this is a legal assignment.
1009: * <p>
1010: * Blank final variables can be set in initializers or constructor
1011: * bodies. In all cases there must be definite single assignment.
1012: * (All instance and instance variable initializers and each
1013: * constructor body are treated as if concatenated for the purposes
1014: * of this check. Assignment to "this.x" is treated as a definite
1015: * assignment to the simple name "x" which names the instance variable.)
1016: */
1017:
1018: public static Vset checkFinalAssign(Environment env, Context ctx,
1019: Vset vset, long where, MemberDefinition field) {
1020: if (field.isBlankFinal()
1021: && field.getClassDefinition() == ctx.field
1022: .getClassDefinition()) {
1023: int number = ctx.getFieldNumber(field);
1024: if (number >= 0 && vset.testVarUnassigned(number)) {
1025: // definite single assignment
1026: vset = vset.addVar(number);
1027: } else {
1028: // it is a blank final in this class, but not assignable
1029: Identifier id = field.getName();
1030: env.error(where, "assign.to.blank.final", id);
1031: }
1032: } else {
1033: // give the generic error message
1034: Identifier id = field.getName();
1035: env.error(where, "assign.to.final", id);
1036: }
1037: return vset;
1038: }
1039:
1040: private static MemberDefinition getClassLiteralCache(
1041: Environment env, Context ctx, String className,
1042: ClassDefinition c) {
1043: // Given a class name, look for a static field to cache it.
1044: // className lname
1045: // pkg.Foo class$pkg$Foo
1046: // [Lpkg.Foo; array$Lpkg$Foo
1047: // [[Lpkg.Foo; array$$Lpkg$Foo
1048: // [I array$I
1049: // [[I array$$I
1050: String lname;
1051: if (!className.startsWith(SIG_ARRAY)) {
1052: lname = prefixClass + className.replace('.', '$');
1053: } else {
1054: lname = prefixArray + className.substring(1);
1055: lname = lname.replace(SIGC_ARRAY, '$'); // [[[I => array$$$I
1056: if (className.endsWith(SIG_ENDCLASS)) {
1057: // [Lpkg.Foo; => array$Lpkg$Foo
1058: lname = lname.substring(0, lname.length() - 1);
1059: lname = lname.replace('.', '$');
1060: }
1061: // else [I => array$I or some such; lname is already OK
1062: }
1063: Identifier fname = Identifier.lookup(lname);
1064:
1065: // The class to put the cache in is now given as an argument.
1066: //
1067: // ClassDefinition c = ctx.field.getClassDefinition();
1068: // while (c.isInnerClass()) {
1069: // c = c.getOuterClass();
1070:
1071: MemberDefinition cfld;
1072: try {
1073: cfld = c.getVariable(env, fname, c);
1074: } catch (ClassNotFound ee) {
1075: return null;
1076: } catch (AmbiguousMember ee) {
1077: return null;
1078: }
1079:
1080: // Ignore inherited field. Each top-level class
1081: // containing a given class literal must have its own copy,
1082: // both for reasons of binary compatibility and to prevent
1083: // access violations should the superclass be in another
1084: // package. Part of fix 4106051.
1085: if (cfld != null && cfld.getClassDefinition() == c) {
1086: return cfld;
1087: }
1088:
1089: // Since each class now has its own copy, we might as well
1090: // tighten up the access to private (previously default).
1091: // Part of fix for 4106051.
1092: // ** Temporarily retract this, as it tickles 4098316.
1093: return env.makeMemberDefinition(env, c.getWhere(), c, null,
1094: M_STATIC | M_SYNTHETIC, // M_PRIVATE,
1095: Type.tClassDesc, fname, null, null, null);
1096: }
1097:
1098: private Expression makeClassLiteralCacheRef(Environment env,
1099: Context ctx, MemberDefinition lookup,
1100: MemberDefinition cfld, String className) {
1101: Expression ccls = new TypeExpression(where, cfld
1102: .getClassDefinition().getType());
1103: Expression cache = new FieldExpression(where, ccls, cfld);
1104: Expression cacheOK = new NotEqualExpression(where, cache
1105: .copyInline(ctx), new NullExpression(where));
1106: Expression lcls = new TypeExpression(where, lookup
1107: .getClassDefinition().getType());
1108: Expression name = new StringExpression(where, className);
1109: Expression namearg[] = { name };
1110: Expression setCache = new MethodExpression(where, lcls, lookup,
1111: namearg);
1112: setCache = new AssignExpression(where, cache.copyInline(ctx),
1113: setCache);
1114: return new ConditionalExpression(where, cacheOK, cache,
1115: setCache);
1116: }
1117:
1118: private Expression makeClassLiteralInlineRef(Environment env,
1119: Context ctx, MemberDefinition lookup, String className) {
1120: Expression lcls = new TypeExpression(where, lookup
1121: .getClassDefinition().getType());
1122: Expression name = new StringExpression(where, className);
1123: Expression namearg[] = { name };
1124: Expression getClass = new MethodExpression(where, lcls, lookup,
1125: namearg);
1126: return getClass;
1127: }
1128:
1129: /**
1130: * Check if constant: Will it inline away?
1131: */
1132: public boolean isConstant() {
1133: if (implementation != null)
1134: return implementation.isConstant();
1135: if ((field != null)
1136: && (right == null || right instanceof TypeExpression || (right.op == THIS && right.where == where))) {
1137: return field.isConstant();
1138: }
1139: return false;
1140: }
1141:
1142: /**
1143: * Inline
1144: */
1145: public Expression inline(Environment env, Context ctx) {
1146: if (implementation != null)
1147: return implementation.inline(env, ctx);
1148: // A field expression may have the side effect of causing
1149: // a NullPointerException, so evaluate it even though
1150: // the value is not needed. Similarly, static field dereferences
1151: // may cause class initialization, so they mustn't be omitted
1152: // either.
1153: //
1154: // However, NullPointerException can't happen and initialization must
1155: // already have occured if you are dotting into 'this'. So
1156: // allow fields of 'this' to be eliminated as a special case.
1157: Expression e = inlineValue(env, ctx);
1158: if (e instanceof FieldExpression) {
1159: FieldExpression fe = (FieldExpression) e;
1160: if ((fe.right != null) && (fe.right.op == THIS))
1161: return null;
1162: // It should be possible to split this into two checks: one using
1163: // isNonNull() for non-statics and a different check for statics.
1164: // That would make the inlining slightly less conservative by
1165: // allowing, for example, dotting into String constants.
1166: }
1167: return e;
1168: }
1169:
1170: public Expression inlineValue(Environment env, Context ctx) {
1171: if (implementation != null)
1172: return implementation.inlineValue(env, ctx);
1173: try {
1174: if (field == null) {
1175: return this ;
1176: }
1177:
1178: if (field.isFinal()) {
1179: Expression e = (Expression) field.getValue(env);
1180: if ((e != null) && e.isConstant()) {
1181: // remove bogus line-number info
1182: e = e.copyInline(ctx);
1183: e.where = where;
1184: return new CommaExpression(where, right, e)
1185: .inlineValue(env, ctx);
1186: }
1187: }
1188:
1189: if (right != null) {
1190: if (field.isStatic()) {
1191: Expression e = right.inline(env, ctx);
1192: right = null;
1193: if (e != null) {
1194: return new CommaExpression(where, e, this );
1195: }
1196: } else {
1197: right = right.inlineValue(env, ctx);
1198: }
1199: }
1200: return this ;
1201:
1202: } catch (ClassNotFound e) {
1203: throw new CompilerError(e);
1204: }
1205: }
1206:
1207: public Expression inlineLHS(Environment env, Context ctx) {
1208: if (implementation != null)
1209: return implementation.inlineLHS(env, ctx);
1210: if (right != null) {
1211: if (field.isStatic()) {
1212: Expression e = right.inline(env, ctx);
1213: right = null;
1214: if (e != null) {
1215: return new CommaExpression(where, e, this );
1216: }
1217: } else {
1218: right = right.inlineValue(env, ctx);
1219: }
1220: }
1221: return this ;
1222: }
1223:
1224: public Expression copyInline(Context ctx) {
1225: if (implementation != null)
1226: return implementation.copyInline(ctx);
1227: return super .copyInline(ctx);
1228: }
1229:
1230: /**
1231: * The cost of inlining this expression
1232: */
1233: public int costInline(int thresh, Environment env, Context ctx) {
1234: if (implementation != null)
1235: return implementation.costInline(thresh, env, ctx);
1236: if (ctx == null) {
1237: return 3 + ((right == null) ? 0 : right.costInline(thresh,
1238: env, ctx));
1239: }
1240: // ctxClass is the current class trying to inline this method
1241: ClassDefinition ctxClass = ctx.field.getClassDefinition();
1242: try {
1243: // We only allow the inlining if the current class can access
1244: // the field, the field's class, and right's declared type.
1245: if (ctxClass.permitInlinedAccess(env, field
1246: .getClassDeclaration())
1247: && ctxClass.permitInlinedAccess(env, field)) {
1248: if (right == null) {
1249: return 3;
1250: } else {
1251: ClassDeclaration rt = env
1252: .getClassDeclaration(right.type);
1253: if (ctxClass.permitInlinedAccess(env, rt)) {
1254: return 3 + right.costInline(thresh, env, ctx);
1255: }
1256: }
1257: }
1258: } catch (ClassNotFound e) {
1259: }
1260: return thresh;
1261: }
1262:
1263: /**
1264: * Code
1265: */
1266: int codeLValue(Environment env, Context ctx, Assembler asm) {
1267: if (implementation != null)
1268: throw new CompilerError("codeLValue");
1269: if (field.isStatic()) {
1270: if (right != null) {
1271: right.code(env, ctx, asm);
1272: return 1;
1273: }
1274: return 0;
1275: }
1276: right.codeValue(env, ctx, asm);
1277: return 1;
1278: }
1279:
1280: void codeLoad(Environment env, Context ctx, Assembler asm) {
1281: if (field == null) {
1282: throw new CompilerError("should not be null");
1283: }
1284: if (field.isStatic()) {
1285: asm.add(where, opc_getstatic, field);
1286: } else {
1287: asm.add(where, opc_getfield, field);
1288: }
1289: }
1290:
1291: void codeStore(Environment env, Context ctx, Assembler asm) {
1292: if (field.isStatic()) {
1293: asm.add(where, opc_putstatic, field);
1294: } else {
1295: asm.add(where, opc_putfield, field);
1296: }
1297: }
1298:
1299: public void codeValue(Environment env, Context ctx, Assembler asm) {
1300: codeLValue(env, ctx, asm);
1301: codeLoad(env, ctx, asm);
1302: }
1303:
1304: /**
1305: * Print
1306: */
1307: public void print(PrintStream out) {
1308: out.print("(");
1309: if (right != null) {
1310: right.print(out);
1311: } else {
1312: out.print("<empty>");
1313: }
1314: out.print("." + id + ")");
1315: if (implementation != null) {
1316: out.print("/IMPL=");
1317: implementation.print(out);
1318: }
1319: }
1320: }
|