Showing the changes to java/pkg/sun/tools/tree/FieldExpression.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,1282 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 1994, 2003, 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 ommitted 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 occured 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 | |||
| 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 | } | ||