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 26package sun.tools.tree; 27 28import sun.tools.java.*; 29import sun.tools.asm.Assembler; 30import sun.tools.asm.LocalVariable; 31import java.io.PrintStream; 32import java.util.Hashtable; 33 34/** 35 * WARNING: The contents of this source file are not part of any 36 * supported API. Code that depends on them does so at its own risk: 37 * they are subject to change or removal without notice. 38 */ 39public 40class IdentifierExpression extends Expression { 41 Identifier id; 42 MemberDefinition field; 43 Expression implementation; 44 45 /** 46 * Constructor 47 */ 48 public IdentifierExpression(long where, Identifier id) { 49 super(IDENT, where, Type.tError); 50 this.id = id; 51 } 52 public IdentifierExpression(IdentifierToken id) { 53 this(id.getWhere(), id.getName()); 54 } 55 public IdentifierExpression(long where, MemberDefinition field) { 56 super(IDENT, where, field.getType()); 57 this.id = field.getName(); 58 this.field = field; 59 } 60 61 public Expression getImplementation() { 62 if (implementation != null) 63 return implementation; 64 return this; 65 } 66 67 /** 68 * Check if the expression is equal to a value 69 */ 70 public boolean equals(Identifier id) { 71 return this.id.equals(id); 72 } 73 74 75 /** 76 * Assign a value to this identifier. [It must already be "bound"] 77 */ 78 private Vset assign(Environment env, Context ctx, Vset vset) { 79 if (field.isLocal()) { 80 LocalMember local = (LocalMember)field; 81 if (local.scopeNumber < ctx.frameNumber) { 82 env.error(where, "assign.to.uplevel", id); 83 } 84 if (local.isFinal()) { 85 // allow definite single assignment of blank finals 86 if (!local.isBlankFinal()) { 87 env.error(where, "assign.to.final", id); 88 } else if (!vset.testVarUnassigned(local.number)) { 89 env.error(where, "assign.to.blank.final", id); 90 } 91 } 92 vset.addVar(local.number); 93 local.writecount++; 94 } else if (field.isFinal()) { 95 vset = FieldExpression.checkFinalAssign(env, ctx, vset, 96 where, field); 97 } 98 return vset; 99 } 100 101 /** 102 * Get the value of this identifier. [ It must already be "bound"] 103 */ 104 private Vset get(Environment env, Context ctx, Vset vset) { 105 if (field.isLocal()) { 106 LocalMember local = (LocalMember)field; 107 if (local.scopeNumber < ctx.frameNumber && !local.isFinal()) { 108 env.error(where, "invalid.uplevel", id); 109 } 110 if (!vset.testVar(local.number)) { 111 env.error(where, "var.not.initialized", id); 112 vset.addVar(local.number); 113 } 114 local.readcount++; 115 } else { 116 if (!field.isStatic()) { 117 if (!vset.testVar(ctx.getThisNumber())) { 118 env.error(where, "access.inst.before.super", id); 119 implementation = null; 120 } 121 } 122 if (field.isBlankFinal()) { 123 int number = ctx.getFieldNumber(field); 124 if (number >= 0 && !vset.testVar(number)) { 125 env.error(where, "var.not.initialized", id); 126 } 127 } 128 } 129 return vset; 130 } 131 132 /** 133 * Bind to a field 134 */ 135 boolean bind(Environment env, Context ctx) { 136 try { 137 field = ctx.getField(env, id); 138 if (field == null) { 139 for (ClassDefinition cdef = ctx.field.getClassDefinition(); 140 cdef != null; cdef = cdef.getOuterClass()) { 141 if (cdef.findAnyMethod(env, id) != null) { 142 env.error(where, "invalid.var", id, 143 ctx.field.getClassDeclaration()); 144 return false; 145 } 146 } 147 env.error(where, "undef.var", id); 148 return false; 149 } 150 151 type = field.getType(); 152 153 // Check access permission 154 if (!ctx.field.getClassDefinition().canAccess(env, field)) { 155 env.error(where, "no.field.access", 156 id, field.getClassDeclaration(), 157 ctx.field.getClassDeclaration()); 158 return false; 159 } 160 161 // Find out how to access this variable. 162 if (field.isLocal()) { 163 LocalMember local = (LocalMember)field; 164 if (local.scopeNumber < ctx.frameNumber) { 165 // get a "val$x" copy via the current object 166 implementation = ctx.makeReference(env, local); 167 } 168 } else { 169 MemberDefinition f = field; 170 171 if (f.reportDeprecated(env)) { 172 env.error(where, "warn.field.is.deprecated", 173 id, f.getClassDefinition()); 174 } 175 176 ClassDefinition fclass = f.getClassDefinition(); 177 if (fclass != ctx.field.getClassDefinition()) { 178 // Maybe an inherited field hides an apparent variable. 179 MemberDefinition f2 = ctx.getApparentField(env, id); 180 if (f2 != null && f2 != f) { 181 ClassDefinition c = ctx.findScope(env, fclass); 182 if (c == null) c = f.getClassDefinition(); 183 if (f2.isLocal()) { 184 env.error(where, "inherited.hides.local", 185 id, c.getClassDeclaration()); 186 } else { 187 env.error(where, "inherited.hides.field", 188 id, c.getClassDeclaration(), 189 f2.getClassDeclaration()); 190 } 191 } 192 } 193 194 // Rewrite as a FieldExpression. 195 // Access methods for private fields, if needed, will be added 196 // during subsequent processing of the FieldExpression. See 197 // method 'FieldExpression.checkCommon'. This division of labor 198 // is somewhat awkward, as most further processing of a 199 // FieldExpression during the checking phase is suppressed when 200 // the referenced field is pre-set as it is here. 201 202 if (f.isStatic()) { 203 Expression base = new TypeExpression(where, 204 f.getClassDeclaration().getType()); 205 implementation = new FieldExpression(where, null, f); 206 } else { 207 Expression base = ctx.findOuterLink(env, where, f); 208 if (base != null) { 209 implementation = new FieldExpression(where, base, f); 210 } 211 } 212 } 213 214 // Check forward reference 215 if (!ctx.canReach(env, field)) { 216 env.error(where, "forward.ref", 217 id, field.getClassDeclaration()); 218 return false; 219 } 220 return true; 221 } catch (ClassNotFound e) { 222 env.error(where, "class.not.found", e.name, ctx.field); 223 } catch (AmbiguousMember e) { 224 env.error(where, "ambig.field", id, 225 e.field1.getClassDeclaration(), 226 e.field2.getClassDeclaration()); 227 } 228 return false; 229 } 230 231 /** 232 * Check expression 233 */ 234 public Vset checkValue(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp) { 235 if (field != null) { 236 // An internally pre-set field, such as an argument copying 237 // an uplevel value. Do not re-check it. 238 return vset; 239 } 240 if (bind(env, ctx)) { 241 vset = get(env, ctx, vset); 242 ctx.field.getClassDefinition().addDependency(field.getClassDeclaration()); 243 if (implementation != null) 244 vset = implementation.checkValue(env, ctx, vset, exp); 245 } 246 return vset; 247 } 248 249 /** 250 * Check the expression if it appears on the LHS of an assignment 251 */ 252 public Vset checkLHS(Environment env, Context ctx, 253 Vset vset, Hashtable<Object, Object> exp) { 254 if (!bind(env, ctx)) 255 return vset; 256 vset = assign(env, ctx, vset); 257 if (implementation != null) 258 vset = implementation.checkValue(env, ctx, vset, exp); 259 return vset; 260 } 261 262 /** 263 * Check the expression if it appears on the LHS of an op= expression 264 */ 265 public Vset checkAssignOp(Environment env, Context ctx, 266 Vset vset, Hashtable<Object, Object> exp, Expression outside) { 267 if (!bind(env, ctx)) 268 return vset; 269 vset = assign(env, ctx, get(env, ctx, vset)); 270 if (implementation != null) 271 vset = implementation.checkValue(env, ctx, vset, exp); 272 return vset; 273 } 274 275 /** 276 * Return an accessor if one is needed for assignments to this expression. 277 */ 278 public FieldUpdater getAssigner(Environment env, Context ctx) { 279 if (implementation != null) 280 return implementation.getAssigner(env, ctx); 281 return null; 282 } 283 284 /** 285 * Return an updater if one is needed for assignments to this expression. 286 */ 287 public FieldUpdater getUpdater(Environment env, Context ctx) { 288 if (implementation != null) 289 return implementation.getUpdater(env, ctx); 290 return null; 291 } 292 293 /** 294 * Check if the present name is part of a scoping prefix. 295 */ 296 public Vset checkAmbigName(Environment env, Context ctx, Vset vset, Hashtable<Object, Object> exp, 297 UnaryExpression loc) { 298 try { 299 if (ctx.getField(env, id) != null) { 300 // if this is a local field, there's nothing more to do. 301 return checkValue(env, ctx, vset, exp); 302 } 303 } catch (ClassNotFound ee) { 304 } catch (AmbiguousMember ee) { 305 } 306 // Can this be interpreted as a type? 307 ClassDefinition c = toResolvedType(env, ctx, true); 308 // Is it a real type?? 309 if (c != null) { 310 loc.right = new TypeExpression(where, c.getType()); 311 return vset; 312 } 313 // We hope it is a package prefix. Let the caller decide. 314 type = Type.tPackage; 315 return vset; 316 } 317 318 /** 319 * Convert an identifier to a known type, or null. 320 */ 321 private ClassDefinition toResolvedType(Environment env, Context ctx, 322 boolean pkgOK) { 323 Identifier rid = ctx.resolveName(env, id); 324 Type t = Type.tClass(rid); 325 if (pkgOK && !env.classExists(t)) { 326 return null; 327 } 328 if (env.resolve(where, ctx.field.getClassDefinition(), t)) { 329 try { 330 ClassDefinition c = env.getClassDefinition(t); 331 332 // Maybe an inherited class hides an apparent class. 333 if (c.isMember()) { 334 ClassDefinition sc = ctx.findScope(env, c.getOuterClass()); 335 if (sc != c.getOuterClass()) { 336 Identifier rid2 = ctx.getApparentClassName(env, id); 337 if (!rid2.equals(idNull) && !rid2.equals(rid)) { 338 env.error(where, "inherited.hides.type", 339 id, sc.getClassDeclaration()); 340 } 341 } 342 } 343 344 if (!c.getLocalName().equals(id.getFlatName().getName())) { 345 env.error(where, "illegal.mangled.name", id, c); 346 } 347 348 return c; 349 } catch (ClassNotFound ee) { 350 } 351 } 352 return null; 353 } 354 355 /** 356 * Convert an identifier to a type. 357 * If one is not known, use the current package as a qualifier. 358 */ 359 Type toType(Environment env, Context ctx) { 360 ClassDefinition c = toResolvedType(env, ctx, false); 361 if (c != null) { 362 return c.getType(); 363 } 364 return Type.tError; 365 } 366 367 /** 368 * Convert an expresion to a type in a context where a qualified 369 * type name is expected, e.g., in the prefix of a qualified type 370 * name. We do not necessarily know where the package prefix ends, 371 * so we operate similarly to 'checkAmbiguousName'. This is the 372 * base case -- the first component of the qualified name. 373 */ 374 /*-------------------------------------------------------* 375 Type toQualifiedType(Environment env, Context ctx) { 376 // We do not look for non-type fields. Is this correct? 377 ClassDefinition c = toResolvedType(env, ctx, true); 378 // Is it a real type? 379 if (c != null) { 380 return c.getType(); 381 } 382 // We hope it is a package prefix. Let the caller decide. 383 return Type.tPackage; 384 } 385 *-------------------------------------------------------*/ 386 387 /** 388 * Check if constant: Will it inline away? 389 */ 390 public boolean isConstant() { 391 if (implementation != null) 392 return implementation.isConstant(); 393 if (field != null) { 394 return field.isConstant(); 395 } 396 return false; 397 } 398 399 /** 400 * Inline 401 */ 402 public Expression inline(Environment env, Context ctx) { 403 return null; 404 } 405 public Expression inlineValue(Environment env, Context ctx) { 406 if (implementation != null) 407 return implementation.inlineValue(env, ctx); 408 if (field == null) { 409 return this; 410 } 411 try { 412 if (field.isLocal()) { 413 if (field.isInlineable(env, false)) { 414 Expression e = (Expression)field.getValue(env); 415 return (e == null) ? this : e.inlineValue(env, ctx); 416 } 417 return this; 418 } 419 return this; 420 } catch (ClassNotFound e) { 421 throw new CompilerError(e); 422 } 423 } 424 public Expression inlineLHS(Environment env, Context ctx) { 425 if (implementation != null) 426 return implementation.inlineLHS(env, ctx); 427 return this; 428 } 429 430 public Expression copyInline(Context ctx) { 431 if (implementation != null) 432 return implementation.copyInline(ctx); 433 IdentifierExpression e = 434 (IdentifierExpression)super.copyInline(ctx); 435 if (field != null && field.isLocal()) { 436 e.field = ((LocalMember)field).getCurrentInlineCopy(ctx); 437 } 438 return e; 439 } 440 441 public int costInline(int thresh, Environment env, Context ctx) { 442 if (implementation != null) 443 return implementation.costInline(thresh, env, ctx); 444 return super.costInline(thresh, env, ctx); 445 } 446 447 /** 448 * Code local vars (object fields have been inlined away) 449 */ 450 int codeLValue(Environment env, Context ctx, Assembler asm) { 451 return 0; 452 } 453 void codeLoad(Environment env, Context ctx, Assembler asm) { 454 asm.add(where, opc_iload + type.getTypeCodeOffset(), 455 ((LocalMember)field).number); 456 } 457 void codeStore(Environment env, Context ctx, Assembler asm) { 458 LocalMember local = (LocalMember)field; 459 asm.add(where, opc_istore + type.getTypeCodeOffset(), 460 new LocalVariable(local, local.number)); 461 } 462 public void codeValue(Environment env, Context ctx, Assembler asm) { 463 codeLValue(env, ctx, asm); 464 codeLoad(env, ctx, asm); 465 } 466 467 /** 468 * Print 469 */ 470 public void print(PrintStream out) { 471 out.print(id + "#" + ((field != null) ? field.hashCode() : 0)); 472 if (implementation != null) { 473 out.print("/IMPL="); 474 implementation.print(out); 475 } 476 } 477} 478