View Javadoc
1   /*
2    * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.tools.tree;
27  
28  import sun.tools.java.*;
29  import sun.tools.asm.*;
30  import java.io.PrintStream;
31  import java.util.Hashtable;
32  
33  /**
34   * WARNING: The contents of this source file are not part of any
35   * supported API.  Code that depends on them does so at its own risk:
36   * they are subject to change or removal without notice.
37   */
38  public
39  class FieldExpression extends UnaryExpression {
40      Identifier id;
41      MemberDefinition field;
42      Expression implementation;
43  
44      // The class from which the field is select ed.
45      ClassDefinition clazz;
46  
47      // For an expression of the form '<class>.super', then
48      // this is <class>, else null.
49      private ClassDefinition superBase;
50  
51      /**
52       * constructor
53       */
54      public FieldExpression(long where, Expression right, Identifier id) {
55          super(FIELD, where, Type.tError, right);
56          this.id = id;
57      }
58      public FieldExpression(long where, Expression right, MemberDefinition field) {
59          super(FIELD, where, field.getType(), right);
60          this.id = field.getName();
61          this.field = field;
62      }
63  
64      public Expression getImplementation() {
65          if (implementation != null)
66              return implementation;
67          return this;
68      }
69  
70      /**
71       * Return true if the field is being selected from
72       * a qualified 'super'.
73       */
74      private boolean isQualSuper() {
75          return superBase != null;
76      }
77  
78      /**
79       * Convert an '.' expression to a qualified identifier
80       */
81      static public Identifier toIdentifier(Expression e) {
82          StringBuffer buf = new StringBuffer();
83          while (e.op == FIELD) {
84              FieldExpression fe = (FieldExpression)e;
85              if (fe.id == idThis || fe.id == idClass) {
86                  return null;
87              }
88              buf.insert(0, fe.id);
89              buf.insert(0, '.');
90              e = fe.right;
91          }
92          if (e.op != IDENT) {
93              return null;
94          }
95          buf.insert(0, ((IdentifierExpression)e).id);
96          return Identifier.lookup(buf.toString());
97      }
98  
99      /**
100      * Convert a qualified name into a type.
101      * Performs a careful check of each inner-class component,
102      * including the JLS 6.6.1 access checks that were omitted
103      * in 'FieldExpression.toType'.
104      * <p>
105      * This code is similar to 'checkCommon', which could be cleaned
106      * up a bit long the lines we have done here.
107      */
108     /*-------------------------------------------------------*
109     Type toQualifiedType(Environment env, Context ctx) {
110         ClassDefinition ctxClass = ctx.field.getClassDefinition();
111         Type rty = right.toQualifiedType(env, ctx);
112         if (rty == Type.tPackage) {
113             // Is this field expression a non-inner type?
114             Identifier nm = toIdentifier(this);
115             if ((nm != null) && env.classExists(nm)) {
116                 Type t = Type.tClass(nm);
117                 if (env.resolve(where, ctxClass, t)) {
118                     return t;
119                 } else {
120                     return null;
121                 }
122             }
123             // Not a type.  Must be a package prefix.
124             return Type.tPackage;
125         }
126         if (rty == null) {
127             // An error was already reported, so quit.
128             return null;
129         }
130 
131         // Check inner-class qualification while unwinding from recursion.
132         try {
133             ClassDefinition rightClass = env.getClassDefinition(rty);
134 
135             // Local variables, which cannot be inner classes,
136             // are ignored here, and thus will not hide inner
137             // classes.  Is this correct?
138             MemberDefinition field = rightClass.getInnerClass(env, id);
139             if (field == null) {
140                 env.error(where, "inner.class.expected", id, rightClass);
141                 return Type.tError;
142             }
143 
144             ClassDefinition innerClass = field.getInnerClass();
145             Type t = innerClass.getType();
146 
147             if (!ctxClass.canAccess(env, field)) {
148                 env.error(where, "no.type.access", id, rightClass, ctxClass);
149                 return t;
150             }
151             if (field.isProtected()
152                 && !ctxClass.protectedAccess(env, field, rty)) {
153                 env.error(where, "invalid.protected.type.use", id, ctxClass, rty);
154                 return t;
155             }
156 
157             // These were omitted earlier in calls to 'toType', but I can't
158             // see any reason for that.  I think it was an oversight.  See
159             // 'checkCommon' and 'checkInnerClass'.
160             innerClass.noteUsedBy(ctxClass, where, env);
161             ctxClass.addDependency(field.getClassDeclaration());
162 
163             return t;
164 
165         } catch (ClassNotFound e) {
166             env.error(where, "class.not.found", e.name, ctx.field);
167         }
168 
169         // Class not found.
170         return null;
171     }
172     *-------------------------------------------------------*/
173 
174     /**
175      * Convert an '.' expression to a type
176      */
177 
178     // This is a rewrite to treat qualified names in a
179     // context in which a type name is expected in the
180     // same way that they are handled for an ambiguous
181     // or expression-expected context in 'checkCommon'
182     // below.  The new code is cleaner and allows better
183     // localization of errors.  Unfortunately, most
184     // qualified names appearing in types are actually
185     // handled by 'Environment.resolve'.  There isn't
186     // much point, then, in breaking out 'toType' as a
187     // special case until the other cases can be cleaned
188     // up as well.  For the time being, we will leave this
189     // code disabled, thus reducing the testing requirements.
190     /*-------------------------------------------------------*
191     Type toType(Environment env, Context ctx) {
192         Type t = toQualifiedType(env, ctx);
193         if (t == null) {
194             return Type.tError;
195         }
196         if (t == Type.tPackage) {
197             FieldExpression.reportFailedPackagePrefix(env, right, true);
198             return Type.tError;
199         }
200         return t;
201     }
202     *-------------------------------------------------------*/
203 
204     Type toType(Environment env, Context ctx) {
205         Identifier id = toIdentifier(this);
206         if (id == null) {
207             env.error(where, "invalid.type.expr");
208             return Type.tError;
209         }
210         Type t = Type.tClass(ctx.resolveName(env, id));
211         if (env.resolve(where, ctx.field.getClassDefinition(), t)) {
212             return t;
213         }
214         return Type.tError;
215     }
216 
217     /**
218      * Check if the present name is part of a scoping prefix.
219      */
220 
221     public Vset checkAmbigName(Environment env, Context ctx,
222                                Vset vset, Hashtable exp,
223                                UnaryExpression loc) {
224         if (id == idThis || id == idClass) {
225             loc = null;         // this cannot be a type or package
226         }
227         return checkCommon(env, ctx, vset, exp, loc, false);
228     }
229 
230     /**
231      * Check the expression
232      */
233 
234     public Vset checkValue(Environment env, Context ctx,
235                            Vset vset, Hashtable exp) {
236         vset = checkCommon(env, ctx, vset, exp, null, false);
237         if (id == idSuper && type != Type.tError) {
238             // "super" is not allowed in this context.
239             // It must always qualify another name.
240             env.error(where, "undef.var.super", idSuper);
241         }
242         return vset;
243     }
244 
245     /**
246      * If 'checkAmbiguousName' returns 'Package.tPackage', then it was
247      * unable to resolve any prefix of the qualified name.  This method
248      * attempts to diagnose the problem.
249      */
250 
251     static void reportFailedPackagePrefix(Environment env, Expression right) {
252         reportFailedPackagePrefix(env, right, false);
253     }
254 
255     static void reportFailedPackagePrefix(Environment env,
256                                           Expression right,
257                                           boolean mustBeType) {
258         // Find the leftmost component, and put the blame on it.
259         Expression idp = right;
260         while (idp instanceof UnaryExpression)
261             idp = ((UnaryExpression)idp).right;
262         IdentifierExpression ie = (IdentifierExpression)idp;
263 
264         // It may be that 'ie' refers to an ambiguous class.  Check this
265         // with a call to env.resolve(). Part of solution for 4059855.
266         try {
267             env.resolve(ie.id);
268         } catch (AmbiguousClass e) {
269             env.error(right.where, "ambig.class", e.name1, e.name2);
270             return;
271         } catch (ClassNotFound e) {
272         }
273 
274         if (idp == right) {
275             if (mustBeType) {
276                 env.error(ie.where, "undef.class", ie.id);
277             } else {
278                 env.error(ie.where, "undef.var.or.class", ie.id);
279             }
280         } else {
281             if (mustBeType) {
282                 env.error(ie.where, "undef.class.or.package", ie.id);
283             } else {
284                 env.error(ie.where, "undef.var.class.or.package", ie.id);
285             }
286         }
287     }
288 
289     /**
290      * Rewrite accesses to private fields of another class.
291      */
292 
293     private Expression
294     implementFieldAccess(Environment env, Context ctx, Expression base, boolean isLHS) {
295         ClassDefinition abase = accessBase(env, ctx);
296         if (abase != null) {
297 
298             // If the field is final and its initializer is a constant expression,
299             // then just rewrite to the constant expression. This is not just an
300             // optimization, but is required for correctness.  If an expression is
301             // rewritten to use an access method, then its status as a constant
302             // expression is lost.  This was the cause of bug 4098737.  Note that
303             // a call to 'getValue(env)' below would not be correct, as it attempts
304             // to simplify the initial value expression, which must not occur until
305             // after the checking phase, for example, after definite assignment checks.
306             if (field.isFinal()) {
307                 Expression e = (Expression)field.getValue();
308                 // Must not be LHS here.  Test as a precaution,
309                 // as we may not be careful to avoid this when
310                 // compiling an erroneous program.
311                 if ((e != null) && e.isConstant() && !isLHS) {
312                     return e.copyInline(ctx);
313                 }
314             }
315 
316             //System.out.println("Finding access method for " + field);
317             MemberDefinition af = abase.getAccessMember(env, ctx, field, isQualSuper());
318             //System.out.println("Using access method " + af);
319 
320             if (!isLHS) {
321                 //System.out.println("Reading " + field +
322                 //                              " via access method " + af);
323                 // If referencing the value of the field, then replace
324                 // with a call to the access method.  If assigning to
325                 // the field, a call to the update method will be
326                 // generated later. It is important that
327                 // 'implementation' not be set to non-null if the
328                 // expression is a valid assignment target.
329                 // (See 'checkLHS'.)
330                 if (field.isStatic()) {
331                     Expression args[] = { };
332                     Expression call =
333                         new MethodExpression(where, null, af, args);
334                     return new CommaExpression(where, base, call);
335                 } else {
336                     Expression args[] = { base };
337                     return new MethodExpression(where, null, af, args);
338                 }
339             }
340         }
341 
342         return null;
343     }
344 
345     /**
346      * Determine if an access method is required, and, if so, return
347      * the class in which it should appear, else return null.
348      */
349     private ClassDefinition accessBase(Environment env, Context ctx) {
350         if (field.isPrivate()) {
351             ClassDefinition cdef = field.getClassDefinition();
352             ClassDefinition ctxClass = ctx.field.getClassDefinition();
353             if (cdef == ctxClass){
354                 // If access from same class as field, then no access
355                 // method is needed.
356                 return null;
357             }
358             // An access method is needed in the class containing the field.
359             return cdef;
360         } else if (field.isProtected()) {
361             if (superBase == null) {
362                 // If access is not via qualified super, then it is either
363                 // OK without an access method, or it is an illegal access
364                 // for which an error message should have been issued.
365                 // Legal accesses include unqualified 'super.foo'.
366                 return null;
367             }
368             ClassDefinition cdef = field.getClassDefinition();
369             ClassDefinition ctxClass = ctx.field.getClassDefinition();
370             if (cdef.inSamePackage(ctxClass)) {
371                 // Access to protected member in same package always allowed.
372                 return null;
373             }
374             // Access via qualified super.
375             // An access method is needed in the qualifying class, an
376             // immediate subclass of the class containing the selected
377             // field.  NOTE: The fact that the returned class is 'superBase'
378             // carries the additional bit of information (that a special
379             // superclass access method is being created) which is provided
380             // to 'getAccessMember' via its 'isSuper' argument.
381             return superBase;
382         } else {
383             // No access method needed.
384             return null;
385         }
386     }
387 
388     /**
389      * Determine if a type is accessible from a given class.
390      */
391     static boolean isTypeAccessible(long where,
392                                     Environment env,
393                                     Type t,
394                                     ClassDefinition c) {
395         switch (t.getTypeCode()) {
396           case TC_CLASS:
397             try {
398                 Identifier nm = t.getClassName();
399                 // Why not just use 'Environment.getClassDeclaration' here?
400                 // But 'Environment.getClassDeclation' has special treatment
401                 // for local classes that is probably necessary.  This code
402                 // was adapted from 'Environment.resolve'.
403                 ClassDefinition def = env.getClassDefinition(t);
404                 return c.canAccess(env, def.getClassDeclaration());
405             } catch (ClassNotFound e) {}  // Ignore -- reported elsewhere.
406             return true;
407           case TC_ARRAY:
408             return isTypeAccessible(where, env, t.getElementType(), c);
409           default:
410             return true;
411         }
412     }
413 
414     /**
415      * Common code for checkValue and checkAmbigName
416      */
417 
418     private Vset checkCommon(Environment env, Context ctx,
419                              Vset vset, Hashtable exp,
420                              UnaryExpression loc, boolean isLHS) {
421 
422         // Handle class literal, e.g., 'x.class'.
423         if (id == idClass) {
424 
425             // In 'x.class', 'x' must be a type name, possibly qualified.
426             Type t = right.toType(env, ctx);
427 
428             if (!t.isType(TC_CLASS) && !t.isType(TC_ARRAY)) {
429                 if (t.isType(TC_ERROR)) {
430                     type = Type.tClassDesc;
431                     return vset;
432                 }
433                 String wrc = null;
434                 switch (t.getTypeCode()) {
435                   case TC_VOID: wrc = "Void"; break;
436                   case TC_BOOLEAN: wrc = "Boolean"; break;
437                   case TC_BYTE: wrc = "Byte"; break;
438                   case TC_CHAR: wrc = "Character"; break;
439                   case TC_SHORT: wrc = "Short"; break;
440                   case TC_INT: wrc = "Integer"; break;
441                   case TC_FLOAT: wrc = "Float"; break;
442                   case TC_LONG: wrc = "Long"; break;
443                   case TC_DOUBLE: wrc = "Double"; break;
444                   default:
445                       env.error(right.where, "invalid.type.expr");
446                       return vset;
447                 }
448                 Identifier wid = Identifier.lookup(idJavaLang+"."+wrc);
449                 Expression wcls = new TypeExpression(where, Type.tClass(wid));
450                 implementation = new FieldExpression(where, wcls, idTYPE);
451                 vset = implementation.checkValue(env, ctx, vset, exp);
452                 type = implementation.type; // java.lang.Class
453                 return vset;
454             }
455 
456             // Check for the bogus type `array of void'
457             if (t.isVoidArray()) {
458                 type = Type.tClassDesc;
459                 env.error(right.where, "void.array");
460                 return vset;
461             }
462 
463             // it is a class or array
464             long fwhere = ctx.field.getWhere();
465             ClassDefinition fcls = ctx.field.getClassDefinition();
466             MemberDefinition lookup = fcls.getClassLiteralLookup(fwhere);
467 
468             String sig = t.getTypeSignature();
469             String className;
470             if (t.isType(TC_CLASS)) {
471                 // sig is like "Lfoo/bar;", name is like "foo.bar".
472                 // We assume SIG_CLASS and SIG_ENDCLASS are 1 char each.
473                 className = sig.substring(1, sig.length()-1)
474                     .replace(SIGC_PACKAGE, '.');
475             } else {
476                 // sig is like "[Lfoo/bar;" or "[I";
477                 // name is like "[Lfoo.bar" or (again) "[I".
478                 className = sig.replace(SIGC_PACKAGE, '.');
479             }
480 
481             if (fcls.isInterface()) {
482                 // The immediately-enclosing type is an interface.
483                 // The class literal can only appear in an initialization
484                 // expression, so don't bother caching it.  (This could
485                 // lose if many initializations use the same class literal,
486                 // but saves time and code space otherwise.)
487                 implementation =
488                     makeClassLiteralInlineRef(env, ctx, lookup, className);
489             } else {
490                 // Cache the call to the helper, as it may be executed
491                 // many times (e.g., if the class literal is inside a loop).
492                 ClassDefinition inClass = lookup.getClassDefinition();
493                 MemberDefinition cfld =
494                     getClassLiteralCache(env, ctx, className, inClass);
495                 implementation =
496                     makeClassLiteralCacheRef(env, ctx, lookup, cfld, className);
497             }
498 
499             vset = implementation.checkValue(env, ctx, vset, exp);
500             type = implementation.type; // java.lang.Class
501             return vset;
502         }
503 
504         // Arrive here if not a class literal.
505 
506         if (field != null) {
507 
508             // The field as been pre-set, e.g., as the result of transforming
509             // an 'IdentifierExpression'. Most error-checking has already been
510             // performed at this point.
511             // QUERY: Why don't we further unify checking of identifier
512             // expressions and field expressions that denote instance and
513             // class variables?
514 
515             implementation = implementFieldAccess(env, ctx, right, isLHS);
516             return (right == null) ?
517                 vset : right.checkAmbigName(env, ctx, vset, exp, this);
518         }
519 
520         // Does the qualifier have a meaning of its own?
521         vset = right.checkAmbigName(env, ctx, vset, exp, this);
522         if (right.type == Type.tPackage) {
523             // Are we out of options?
524             if (loc == null) {
525                 FieldExpression.reportFailedPackagePrefix(env, right);
526                 return vset;
527             }
528 
529             // ASSERT(loc.right == this)
530 
531             // Nope.  Is this field expression a type?
532             Identifier nm = toIdentifier(this);
533             if ((nm != null) && env.classExists(nm)) {
534                 loc.right = new TypeExpression(where, Type.tClass(nm));
535                 // Check access. (Cf. IdentifierExpression.toResolvedType.)
536                 ClassDefinition ctxClass = ctx.field.getClassDefinition();
537                 env.resolve(where, ctxClass, loc.right.type);
538                 return vset;
539             }
540 
541             // Let the caller make sense of it, then.
542             type = Type.tPackage;
543             return vset;
544         }
545 
546         // Good; we have a well-defined qualifier type.
547 
548         ClassDefinition ctxClass = ctx.field.getClassDefinition();
549         boolean staticRef = (right instanceof TypeExpression);
550 
551         try {
552 
553             // Handle array 'length' field, e.g., 'x.length'.
554 
555             if (!right.type.isType(TC_CLASS)) {
556                 if (right.type.isType(TC_ARRAY) && id.equals(idLength)) {
557                     // Verify that the type of the base expression is accessible.
558                     // Required by JLS 6.6.1.  Fixes 4094658.
559                     if (!FieldExpression.isTypeAccessible(where, env, right.type, ctxClass)) {
560                         ClassDeclaration cdecl = ctxClass.getClassDeclaration();
561                         if (staticRef) {
562                             env.error(where, "no.type.access",
563                                       id, right.type.toString(), cdecl);
564                         } else {
565                             env.error(where, "cant.access.member.type",
566                                       id, right.type.toString(), cdecl);
567                         }
568                     }
569                     type = Type.tInt;
570                     implementation = new LengthExpression(where, right);
571                     return vset;
572                 }
573                 if (!right.type.isType(TC_ERROR)) {
574                     env.error(where, "invalid.field.reference", id, right.type);
575                 }
576                 return vset;
577             }
578 
579             // At this point, we know that 'right.type' is a class type.
580 
581             // Note that '<expr>.super(...)' and '<expr>.this(...)' cases never
582             // reach here.  Instead, '<expr>' is stored as the 'outerArg' field
583             // of a 'SuperExpression' or 'ThisExpression' node.
584 
585             // If our prefix is of the form '<class>.super', then we are
586             // about to do a field selection '<class>.super.<field>'.
587             // Save the qualifying class in 'superBase', which is non-null
588             // only if the current FieldExpression is a qualified 'super' form.
589             // Also, set 'sourceClass' to the "effective accessing class" relative
590             // to which access checks will be performed.  Normally, this is the
591             // immediately enclosing class.  For '<class>.this' and '<class>.super',
592             // however, we use <class>.
593 
594             ClassDefinition sourceClass = ctxClass;
595             if (right instanceof FieldExpression) {
596                 Identifier id = ((FieldExpression)right).id;
597                 if (id == idThis) {
598                     sourceClass = ((FieldExpression)right).clazz;
599                 } else if (id == idSuper) {
600                     sourceClass = ((FieldExpression)right).clazz;
601                     superBase = sourceClass;
602                 }
603             }
604 
605             // Handle 'class.this' and 'class.super'.
606             //
607             // Suppose 'super.name' appears within a class C with immediate
608             // superclass S. According to JLS 15.10.2, 'super.name' in this
609             // case is equivalent to '((S)this).name'.  Analogously, we interpret
610             // 'class.super.name' as '((S)(class.this)).name', where S is the
611             // immediate superclass of (enclosing) class 'class'.
612             // Note that 'super' may not stand alone as an expression, but must
613             // occur as the qualifying expression of a field access or a method
614             // invocation.  This is enforced in 'SuperExpression.checkValue' and
615             // 'FieldExpression.checkValue', and need not concern us here.
616 
617             //ClassDefinition clazz = env.getClassDefinition(right.type);
618             clazz = env.getClassDefinition(right.type);
619             if (id == idThis || id == idSuper) {
620                 if (!staticRef) {
621                     env.error(right.where, "invalid.type.expr");
622                 }
623 
624                 // We used to check that 'right.type' is accessible here,
625                 // per JLS 6.6.1.  As a result of the fix for 4102393, however,
626                 // the qualifying class name must exactly match an enclosing
627                 // outer class, which is necessarily accessible.
628 
629                 /*** Temporary assertion check ***/
630                 if (ctx.field.isSynthetic())
631                     throw new CompilerError("synthetic qualified this");
632                 /*********************************/
633 
634                 // A.this means we're inside an A and we want its self ptr.
635                 // C.this is always the same as this when C is innermost.
636                 // Another A.this means we skip out to get a "hidden" this,
637                 // just as ASuper.foo skips out to get a hidden variable.
638                 // Last argument 'true' means we want an exact class match,
639                 // not a subclass of the specified class ('clazz').
640                 implementation = ctx.findOuterLink(env, where, clazz, null, true);
641                 vset = implementation.checkValue(env, ctx, vset, exp);
642                 if (id == idSuper) {
643                     type = clazz.getSuperClass().getType();
644                 } else {
645                     type = clazz.getType();
646                 }
647                 return vset;
648             }
649 
650             // Field should be an instance variable or class variable.
651             field = clazz.getVariable(env, id, sourceClass);
652 
653             if (field == null && staticRef && loc != null) {
654                 // Is this field expression an inner type?
655                 // Search the class and its supers (but not its outers).
656                 // QUERY: We may need to get the inner class from a
657                 // superclass of 'clazz'.  This call is prepared to
658                 // resolve the superclass if necessary.  Can we arrange
659                 // to assure that it is always previously resolved?
660                 // This is one of a small number of problematic calls that
661                 // requires 'getSuperClass' to resolve superclasses on demand.
662                 // See 'ClassDefinition.getInnerClass(env, nm)'.
663                 field = clazz.getInnerClass(env, id);
664                 if (field != null) {
665                     return checkInnerClass(env, ctx, vset, exp, loc);
666                 }
667             }
668 
669             // If not a variable reference, diagnose error if name is
670             // that of a method.
671 
672             if (field == null) {
673                 if ((field = clazz.findAnyMethod(env, id)) != null) {
674                     env.error(where, "invalid.field",
675                               id, field.getClassDeclaration());
676                 } else {
677                     env.error(where, "no.such.field", id, clazz);
678                 }
679                 return vset;
680             }
681 
682             // At this point, we have identified a valid field.
683 
684             // Required by JLS 6.6.1.  Fixes 4094658.
685             if (!FieldExpression.isTypeAccessible(where, env, right.type, sourceClass)) {
686                 ClassDeclaration cdecl = sourceClass.getClassDeclaration();
687                 if (staticRef) {
688                     env.error(where, "no.type.access",
689                               id, right.type.toString(), cdecl);
690                 } else {
691                     env.error(where, "cant.access.member.type",
692                               id, right.type.toString(), cdecl);
693                 }
694             }
695 
696             type = field.getType();
697 
698             if (!sourceClass.canAccess(env, field)) {
699                 env.error(where, "no.field.access",
700                           id, clazz, sourceClass.getClassDeclaration());
701                 return vset;
702             }
703 
704             if (staticRef && !field.isStatic()) {
705                 // 'Class.field' is not legal when field is not static;
706                 // see JLS 15.13.1.  This case was permitted by javac
707                 // prior to 1.2; static refs were silently changed to
708                 // be dynamic access of the form 'this.field'.
709                 env.error(where, "no.static.field.access", id, clazz);
710                 return vset;
711             } else {
712                 // Rewrite access to use an access method if necessary.
713                 implementation = implementFieldAccess(env, ctx, right, isLHS);
714             }
715 
716             // Check for invalid access to protected field.
717             if (field.isProtected()
718                 && !(right instanceof SuperExpression
719                      // Extension of JLS 6.6.2 for qualified 'super'.
720                      || (right instanceof FieldExpression &&
721                          ((FieldExpression)right).id == idSuper))
722                 && !sourceClass.protectedAccess(env, field, right.type)) {
723                 env.error(where, "invalid.protected.field.use",
724                           field.getName(), field.getClassDeclaration(),
725                           right.type);
726                 return vset;
727             }
728 
729             if ((!field.isStatic()) &&
730                 (right.op == THIS) && !vset.testVar(ctx.getThisNumber())) {
731                 env.error(where, "access.inst.before.super", id);
732             }
733 
734             if (field.reportDeprecated(env)) {
735                 env.error(where, "warn."+"field.is.deprecated",
736                           id, field.getClassDefinition());
737             }
738 
739             // When a package-private class defines public or protected
740             // members, those members may sometimes be accessed from
741             // outside of the package in public subclasses.  In these
742             // cases, we need to massage the getField to refer to
743             // to an accessible subclass rather than the package-private
744             // parent class.  Part of fix for 4135692.
745 
746             // Find out if the class which contains this field
747             // reference has access to the class which declares the
748             // public or protected field.
749             if (sourceClass == ctxClass) {
750                 ClassDefinition declarer = field.getClassDefinition();
751                 if (declarer.isPackagePrivate() &&
752                     !declarer.getName().getQualifier()
753                     .equals(sourceClass.getName().getQualifier())) {
754 
755                     //System.out.println("The access of member " +
756                     //             field + " declared in class " +
757                     //             declarer +
758                     //             " is not allowed by the VM from class  " +
759                     //             ctxClass +
760                     //             ".  Replacing with an access of class " +
761                     //             clazz);
762 
763                     // We cannot make this access at the VM level.
764                     // Construct a member which will stand for this
765                     // field in ctxClass and set `field' to refer to it.
766                     field =
767                         MemberDefinition.makeProxyMember(field, clazz, env);
768                 }
769             }
770 
771             sourceClass.addDependency(field.getClassDeclaration());
772 
773         } catch (ClassNotFound e) {
774             env.error(where, "class.not.found", e.name, ctx.field);
775 
776         } catch (AmbiguousMember e) {
777             env.error(where, "ambig.field",
778                       id, e.field1.getClassDeclaration(), e.field2.getClassDeclaration());
779         }
780         return vset;
781     }
782 
783     /**
784      * Return a <code>FieldUpdater</code> object to be used in updating the
785      * value of the location denoted by <code>this</code>, which must be an
786      * expression suitable for the left-hand side of an assignment.
787      * This is used for implementing assignments to private fields for which
788      * an access method is required.  Returns null if no access method is
789      * needed, in which case the assignment is handled in the usual way, by
790      * direct access.  Only simple assignment expressions are handled here
791      * Assignment operators and pre/post increment/decrement operators are
792      * are handled by 'getUpdater' below.
793      * <p>
794      * Must be called after 'checkValue', else 'right' will be invalid.
795      */
796 
797 
798     public FieldUpdater getAssigner(Environment env, Context ctx) {
799         if (field == null) {
800             // Field can legitimately be null if the field name was
801             // undefined, in which case an error was reported, but
802             // no value for 'field' is available.
803             //   throw new CompilerError("getAssigner");
804             return null;
805         }
806         ClassDefinition abase = accessBase(env, ctx);
807         if (abase != null) {
808             MemberDefinition setter = abase.getUpdateMember(env, ctx, field, isQualSuper());
809             // It may not be necessary to copy 'right' here.
810             Expression base = (right == null) ? null : right.copyInline(ctx);
811             // Created 'FieldUpdater' has no getter method.
812             return new FieldUpdater(where, field, base, null, setter);
813         }
814         return null;
815     }
816 
817     /**
818      * Return a <code>FieldUpdater</code> object to be used in updating the
819      * value of the location denoted by <code>this</code>, which must be an
820      * expression suitable for the left-hand side of an assignment.  This is
821      * used for implementing the assignment operators and the increment and
822      * decrement operators on private fields that are accessed from another
823      * class, e.g, uplevel from an inner class. Returns null if no access
824      * method is needed.
825      * <p>
826      * Must be called after 'checkValue', else 'right' will be invalid.
827      */
828 
829     public FieldUpdater getUpdater(Environment env, Context ctx) {
830         if (field == null) {
831             // Field can legitimately be null if the field name was
832             // undefined, in which case an error was reported, but
833             // no value for 'field' is available.
834             //   throw new CompilerError("getUpdater");
835             return null;
836         }
837         ClassDefinition abase = accessBase(env, ctx);
838         if (abase != null) {
839             MemberDefinition getter = abase.getAccessMember(env, ctx, field, isQualSuper());
840             MemberDefinition setter = abase.getUpdateMember(env, ctx, field, isQualSuper());
841             // It may not be necessary to copy 'right' here.
842             Expression base = (right == null) ? null : right.copyInline(ctx);
843             return new FieldUpdater(where, field, base, getter, setter);
844         }
845         return null;
846     }
847 
848     /**
849      * This field expression is an inner class reference.
850      * Finish checking it.
851      */
852     private Vset checkInnerClass(Environment env, Context ctx,
853                                  Vset vset, Hashtable exp,
854                                  UnaryExpression loc) {
855         ClassDefinition inner = field.getInnerClass();
856         type = inner.getType();
857 
858         if (!inner.isTopLevel()) {
859             env.error(where, "inner.static.ref", inner.getName());
860         }
861 
862         Expression te = new TypeExpression(where, type);
863 
864         // check access
865         ClassDefinition ctxClass = ctx.field.getClassDefinition();
866         try {
867             if (!ctxClass.canAccess(env, field)) {
868                 ClassDefinition clazz = env.getClassDefinition(right.type);
869                 //env.error(where, "no.type.access",
870                 //          id, clazz, ctx.field.getClassDeclaration());
871                 env.error(where, "no.type.access",
872                           id, clazz, ctxClass.getClassDeclaration());
873                 return vset;
874             }
875 
876             if (field.isProtected()
877                 && !(right instanceof SuperExpression
878                      // Extension of JLS 6.6.2 for qualified 'super'.
879                      || (right instanceof FieldExpression &&
880                          ((FieldExpression)right).id == idSuper))
881                 && !ctxClass.protectedAccess(env, field, right.type)){
882                 env.error(where, "invalid.protected.field.use",
883                           field.getName(), field.getClassDeclaration(),
884                           right.type);
885                 return vset;
886             }
887 
888             inner.noteUsedBy(ctxClass, where, env);
889 
890         } catch (ClassNotFound e) {
891             env.error(where, "class.not.found", e.name, ctx.field);
892         }
893 
894         ctxClass.addDependency(field.getClassDeclaration());
895         if (loc == null)
896             // Complain about a free-floating type name.
897             return te.checkValue(env, ctx, vset, exp);
898         loc.right = te;
899         return vset;
900     }
901 
902     /**
903      * Check the expression if it appears on the LHS of an assignment
904      */
905     public Vset checkLHS(Environment env, Context ctx,
906                          Vset vset, Hashtable exp) {
907         boolean hadField = (field != null);
908 
909         //checkValue(env, ctx, vset, exp);
910         checkCommon(env, ctx, vset, exp, null, true);
911 
912         // If 'implementation' is set to a non-null value, then the
913         // field expression does not denote an assignable location,
914         // e.g., the 'length' field of an array.
915         if (implementation != null) {
916             // This just reports an error and recovers.
917             return super.checkLHS(env, ctx, vset, exp);
918         }
919 
920         if (field != null && field.isFinal() && !hadField) {
921             if (field.isBlankFinal()) {
922                 if (field.isStatic()) {
923                     if (right != null) {
924                         env.error(where, "qualified.static.final.assign");
925                     }
926                     // Continue with checking anyhow.
927                     // In fact, it would be easy to allow this case.
928                 } else {
929                     if ((right != null) && (right.op != THIS)) {
930                         env.error(where, "bad.qualified.final.assign", field.getName());
931                         // The actual instance could be anywhere, so don't
932                         // continue with checking the definite assignment status.
933                         return vset;
934                     }
935                 }
936                 vset = checkFinalAssign(env, ctx, vset, where, field);
937             } else {
938                 env.error(where, "assign.to.final", id);
939             }
940         }
941         return vset;
942     }
943 
944     /**
945      * Check the expression if it appears on the LHS of an op= expression
946      */
947     public Vset checkAssignOp(Environment env, Context ctx,
948                               Vset vset, Hashtable exp, Expression outside) {
949 
950         //checkValue(env, ctx, vset, exp);
951         checkCommon(env, ctx, vset, exp, null, true);
952 
953         // If 'implementation' is set to a non-null value, then the
954         // field expression does not denote an assignable location,
955         // e.g., the 'length' field of an array.
956         if (implementation != null) {
957             return super.checkLHS(env, ctx, vset, exp);
958         }
959         if (field != null && field.isFinal()) {
960             env.error(where, "assign.to.final", id);
961         }
962         return vset;
963     }
964 
965     /**
966      * There is a simple assignment being made to the given final field.
967      * The field was named either by a simple name or by an almost-simple
968      * expression of the form "this.v".
969      * Check if this is a legal assignment.
970      * <p>
971      * Blank final variables can be set in initializers or constructor
972      * bodies.  In all cases there must be definite single assignment.
973      * (All instance and instance variable initializers and each
974      * constructor body are treated as if concatenated for the purposes
975      * of this check.  Assignment to "this.x" is treated as a definite
976      * assignment to the simple name "x" which names the instance variable.)
977      */
978 
979     public static Vset checkFinalAssign(Environment env, Context ctx,
980                                         Vset vset, long where,
981                                         MemberDefinition field) {
982         if (field.isBlankFinal()
983             && field.getClassDefinition() == ctx.field.getClassDefinition()) {
984             int number = ctx.getFieldNumber(field);
985             if (number >= 0 && vset.testVarUnassigned(number)) {
986                 // definite single assignment
987                 vset = vset.addVar(number);
988             } else {
989                 // it is a blank final in this class, but not assignable
990                 Identifier id = field.getName();
991                 env.error(where, "assign.to.blank.final", id);
992             }
993         } else {
994             // give the generic error message
995             Identifier id = field.getName();
996             env.error(where, "assign.to.final", id);
997         }
998         return vset;
999     }
1000 
1001     private static MemberDefinition getClassLiteralCache(Environment env,
1002                                                          Context ctx,
1003                                                          String className,
1004                                                          ClassDefinition c) {
1005         // Given a class name, look for a static field to cache it.
1006         //      className       lname
1007         //      pkg.Foo         class$pkg$Foo
1008         //      [Lpkg.Foo;      array$Lpkg$Foo
1009         //      [[Lpkg.Foo;     array$$Lpkg$Foo
1010         //      [I              array$I
1011         //      [[I             array$$I
1012         String lname;
1013         if (!className.startsWith(SIG_ARRAY)) {
1014             lname = prefixClass + className.replace('.', '$');
1015         } else {
1016             lname = prefixArray + className.substring(1);
1017             lname = lname.replace(SIGC_ARRAY, '$'); // [[[I => array$$$I
1018             if (className.endsWith(SIG_ENDCLASS)) {
1019                 // [Lpkg.Foo; => array$Lpkg$Foo
1020                 lname = lname.substring(0, lname.length() - 1);
1021                 lname = lname.replace('.', '$');
1022             }
1023             // else [I => array$I or some such; lname is already OK
1024         }
1025         Identifier fname = Identifier.lookup(lname);
1026 
1027         // The class to put the cache in is now given as an argument.
1028         //
1029         // ClassDefinition c = ctx.field.getClassDefinition();
1030         // while (c.isInnerClass()) {
1031         //     c = c.getOuterClass();
1032 
1033         MemberDefinition cfld;
1034         try {
1035             cfld = c.getVariable(env, fname, c);
1036         } catch (ClassNotFound ee) {
1037             return null;
1038         } catch (AmbiguousMember ee) {
1039             return null;
1040         }
1041 
1042         // Ignore inherited field.  Each top-level class
1043         // containing a given class literal must have its own copy,
1044         // both for reasons of binary compatibility and to prevent
1045         // access violations should the superclass be in another
1046         // package.  Part of fix 4106051.
1047         if (cfld != null && cfld.getClassDefinition() == c) {
1048             return cfld;
1049         }
1050 
1051         // Since each class now has its own copy, we might as well
1052         // tighten up the access to private (previously default).
1053         // Part of fix for 4106051.
1054         // ** Temporarily retract this, as it tickles 4098316.
1055         return env.makeMemberDefinition(env, c.getWhere(),
1056                                         c, null,
1057                                         M_STATIC | M_SYNTHETIC, // M_PRIVATE,
1058                                         Type.tClassDesc, fname,
1059                                         null, null, null);
1060     }
1061 
1062     private Expression makeClassLiteralCacheRef(Environment env, Context ctx,
1063                                                 MemberDefinition lookup,
1064                                                 MemberDefinition cfld,
1065                                                 String className) {
1066         Expression ccls = new TypeExpression(where,
1067                                              cfld.getClassDefinition()
1068                                              .getType());
1069         Expression cache = new FieldExpression(where, ccls, cfld);
1070         Expression cacheOK =
1071             new NotEqualExpression(where, cache.copyInline(ctx),
1072                                    new NullExpression(where));
1073         Expression lcls =
1074             new TypeExpression(where, lookup.getClassDefinition() .getType());
1075         Expression name = new StringExpression(where, className);
1076         Expression namearg[] = { name };
1077         Expression setCache = new MethodExpression(where, lcls,
1078                                                    lookup, namearg);
1079         setCache = new AssignExpression(where, cache.copyInline(ctx),
1080                                         setCache);
1081         return new ConditionalExpression(where, cacheOK, cache, setCache);
1082     }
1083 
1084     private Expression makeClassLiteralInlineRef(Environment env, Context ctx,
1085                                                  MemberDefinition lookup,
1086                                                  String className) {
1087         Expression lcls =
1088             new TypeExpression(where, lookup.getClassDefinition().getType());
1089         Expression name = new StringExpression(where, className);
1090         Expression namearg[] = { name };
1091         Expression getClass = new MethodExpression(where, lcls,
1092                                                    lookup, namearg);
1093         return getClass;
1094     }
1095 
1096 
1097     /**
1098      * Check if constant:  Will it inline away?
1099      */
1100     public boolean isConstant() {
1101         if (implementation != null)
1102             return implementation.isConstant();
1103         if ((field != null)
1104             && (right == null || right instanceof TypeExpression
1105                 || (right.op == THIS && right.where == where))) {
1106             return field.isConstant();
1107         }
1108         return false;
1109     }
1110 
1111     /**
1112      * Inline
1113      */
1114     public Expression inline(Environment env, Context ctx) {
1115         if (implementation != null)
1116             return implementation.inline(env, ctx);
1117         // A field expression may have the side effect of causing
1118         // a NullPointerException, so evaluate it even though
1119         // the value is not needed.  Similarly, static field dereferences
1120         // may cause class initialization, so they mustn't be omitted
1121         // either.
1122         //
1123         // However, NullPointerException can't happen and initialization must
1124         // already have occurred if you are dotting into 'this'.  So
1125         // allow fields of 'this' to be eliminated as a special case.
1126         Expression e = inlineValue(env, ctx);
1127         if (e instanceof FieldExpression) {
1128             FieldExpression fe = (FieldExpression) e;
1129             if ((fe.right != null) && (fe.right.op==THIS))
1130                 return null;
1131             // It should be possible to split this into two checks: one using
1132             // isNonNull() for non-statics and a different check for statics.
1133             // That would make the inlining slightly less conservative by
1134             // allowing, for example, dotting into String constants.
1135             }
1136         return e;
1137     }
1138     public Expression inlineValue(Environment env, Context ctx) {
1139         if (implementation != null)
1140             return implementation.inlineValue(env, ctx);
1141         try {
1142             if (field == null) {
1143                 return this;
1144             }
1145 
1146             if (field.isFinal()) {
1147                 Expression e = (Expression)field.getValue(env);
1148                 if ((e != null) && e.isConstant()) {
1149                     // remove bogus line-number info
1150                     e = e.copyInline(ctx);
1151                     e.where = where;
1152                     return new CommaExpression(where, right, e).inlineValue(env, ctx);
1153                 }
1154             }
1155 
1156             if (right != null) {
1157                 if (field.isStatic()) {
1158                     Expression e = right.inline(env, ctx);
1159                     right = null;
1160                     if (e != null) {
1161                         return new CommaExpression(where, e, this);
1162                     }
1163                 } else {
1164                     right = right.inlineValue(env, ctx);
1165                 }
1166             }
1167             return this;
1168 
1169         } catch (ClassNotFound e) {
1170             throw new CompilerError(e);
1171         }
1172     }
1173     public Expression inlineLHS(Environment env, Context ctx) {
1174         if (implementation != null)
1175             return implementation.inlineLHS(env, ctx);
1176         if (right != null) {
1177             if (field.isStatic()) {
1178                 Expression e = right.inline(env, ctx);
1179                 right = null;
1180                 if (e != null) {
1181                     return new CommaExpression(where, e, this);
1182                 }
1183             } else {
1184                 right = right.inlineValue(env, ctx);
1185             }
1186         }
1187         return this;
1188     }
1189 
1190     public Expression copyInline(Context ctx) {
1191         if (implementation != null)
1192             return implementation.copyInline(ctx);
1193         return super.copyInline(ctx);
1194     }
1195 
1196     /**
1197      * The cost of inlining this expression
1198      */
1199     public int costInline(int thresh, Environment env, Context ctx) {
1200         if (implementation != null)
1201             return implementation.costInline(thresh, env, ctx);
1202         if (ctx == null) {
1203             return 3 + ((right == null) ? 0
1204                                         : right.costInline(thresh, env, ctx));
1205         }
1206         // ctxClass is the current class trying to inline this method
1207         ClassDefinition ctxClass = ctx.field.getClassDefinition();
1208         try {
1209             // We only allow the inlining if the current class can access
1210             // the field, the field's class, and right's declared type.
1211             if (    ctxClass.permitInlinedAccess(env, field.getClassDeclaration())
1212                  && ctxClass.permitInlinedAccess(env, field)) {
1213                 if (right == null) {
1214                     return 3;
1215                 } else {
1216                     ClassDeclaration rt = env.getClassDeclaration(right.type);
1217                     if (ctxClass.permitInlinedAccess(env, rt)) {
1218                         return 3 + right.costInline(thresh, env, ctx);
1219                     }
1220                 }
1221             }
1222         } catch (ClassNotFound e) {
1223         }
1224         return thresh;
1225     }
1226 
1227     /**
1228      * Code
1229      */
1230     int codeLValue(Environment env, Context ctx, Assembler asm) {
1231         if (implementation != null)
1232             throw new CompilerError("codeLValue");
1233         if (field.isStatic()) {
1234             if (right != null) {
1235                 right.code(env, ctx, asm);
1236                 return 1;
1237             }
1238             return 0;
1239         }
1240         right.codeValue(env, ctx, asm);
1241         return 1;
1242     }
1243     void codeLoad(Environment env, Context ctx, Assembler asm) {
1244         if (field == null) {
1245             throw new CompilerError("should not be null");
1246         }
1247         if (field.isStatic()) {
1248             asm.add(where, opc_getstatic, field);
1249         } else {
1250             asm.add(where, opc_getfield, field);
1251         }
1252     }
1253     void codeStore(Environment env, Context ctx, Assembler asm) {
1254         if (field.isStatic()) {
1255             asm.add(where, opc_putstatic, field);
1256         } else {
1257             asm.add(where, opc_putfield, field);
1258         }
1259     }
1260 
1261     public void codeValue(Environment env, Context ctx, Assembler asm) {
1262         codeLValue(env, ctx, asm);
1263         codeLoad(env, ctx, asm);
1264     }
1265 
1266     /**
1267      * Print
1268      */
1269     public void print(PrintStream out) {
1270         out.print("(");
1271         if (right != null) {
1272             right.print(out);
1273         } else {
1274             out.print("<empty>");
1275         }
1276         out.print("." + id + ")");
1277         if (implementation != null) {
1278             out.print("/IMPL=");
1279             implementation.print(out);
1280         }
1281     }
1282 }