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 */ FieldExpression(long where, Expression right, Identifier id)54 public FieldExpression(long where, Expression right, Identifier id) { 55 super(FIELD, where, Type.tError, right); 56 this.id = id; 57 } FieldExpression(long where, Expression right, MemberDefinition field)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 getImplementation()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 */ isQualSuper()74 private boolean isQualSuper() { 75 return superBase != null; 76 } 77 78 /** 79 * Convert an '.' expression to a qualified identifier 80 */ toIdentifier(Expression e)81 static public Identifier toIdentifier(Expression e) { 82 StringBuilder sb = new StringBuilder(); 83 while (e.op == FIELD) { 84 FieldExpression fe = (FieldExpression)e; 85 if (fe.id == idThis || fe.id == idClass) { 86 return null; 87 } 88 sb.insert(0, fe.id); 89 sb.insert(0, '.'); 90 e = fe.right; 91 } 92 if (e.op != IDENT) { 93 return null; 94 } 95 sb.insert(0, ((IdentifierExpression) e).id); 96 return Identifier.lookup(sb.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 toType(Environment env, Context ctx)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 checkAmbigName(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp, UnaryExpression loc)221 public Vset checkAmbigName(Environment env, Context ctx, 222 Vset vset, Hashtable<Object, Object> 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 checkValue(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp)234 public Vset checkValue(Environment env, Context ctx, 235 Vset vset, Hashtable<Object, Object> 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 reportFailedPackagePrefix(Environment env, Expression right)251 static void reportFailedPackagePrefix(Environment env, Expression right) { 252 reportFailedPackagePrefix(env, right, false); 253 } 254 reportFailedPackagePrefix(Environment env, Expression right, boolean mustBeType)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 implementFieldAccess(Environment env, Context ctx, Expression base, boolean isLHS)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 */ accessBase(Environment env, Context ctx)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 */ isTypeAccessible(long where, Environment env, Type t, ClassDefinition c)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 checkCommon(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp, UnaryExpression loc, boolean isLHS)418 private Vset checkCommon(Environment env, Context ctx, 419 Vset vset, Hashtable<Object, Object> 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 getAssigner(Environment env, Context ctx)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 getUpdater(Environment env, Context ctx)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 */ checkInnerClass(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp, UnaryExpression loc)852 private Vset checkInnerClass(Environment env, Context ctx, 853 Vset vset, Hashtable<Object, Object> 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 */ checkLHS(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp)905 public Vset checkLHS(Environment env, Context ctx, 906 Vset vset, Hashtable<Object, Object> 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 */ checkAssignOp(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp, Expression outside)947 public Vset checkAssignOp(Environment env, Context ctx, 948 Vset vset, Hashtable<Object, Object> 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 checkFinalAssign(Environment env, Context ctx, Vset vset, long where, MemberDefinition field)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 getClassLiteralCache(Environment env, Context ctx, String className, ClassDefinition c)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 makeClassLiteralCacheRef(Environment env, Context ctx, MemberDefinition lookup, MemberDefinition cfld, String className)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 makeClassLiteralInlineRef(Environment env, Context ctx, MemberDefinition lookup, String className)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 */ isConstant()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 */ inline(Environment env, Context ctx)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 } inlineValue(Environment env, Context ctx)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 } inlineLHS(Environment env, Context ctx)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 copyInline(Context ctx)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 */ costInline(int thresh, Environment env, Context ctx)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 */ codeLValue(Environment env, Context ctx, Assembler asm)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 } codeLoad(Environment env, Context ctx, Assembler asm)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 } codeStore(Environment env, Context ctx, Assembler asm)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 codeValue(Environment env, Context ctx, Assembler asm)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 */ print(PrintStream out)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 } 1283