1 /******************************************************************************* 2 * Copyright (c) 2000, 2003 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Common Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/cpl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 *******************************************************************************/ 11 package net.sourceforge.phpdt.internal.compiler.ast; 12 13 import net.sourceforge.phpdt.internal.compiler.ASTVisitor; 14 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext; 15 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo; 16 import net.sourceforge.phpdt.internal.compiler.impl.Constant; 17 import net.sourceforge.phpdt.internal.compiler.lookup.BindingIds; 18 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope; 19 import net.sourceforge.phpdt.internal.compiler.lookup.FieldBinding; 20 import net.sourceforge.phpdt.internal.compiler.lookup.InvocationSite; 21 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding; 22 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding; 23 import net.sourceforge.phpdt.internal.compiler.lookup.Scope; 24 import net.sourceforge.phpdt.internal.compiler.lookup.SourceTypeBinding; 25 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding; 26 27 public class FieldReference extends Reference implements InvocationSite { 28 29 public Expression receiver; 30 31 public char[] token; 32 33 public FieldBinding binding, codegenBinding; 34 35 public long nameSourcePosition; // (start<<32)+end 36 37 MethodBinding syntheticReadAccessor, syntheticWriteAccessor; 38 39 public TypeBinding receiverType; 40 FieldReference(char[] source, long pos)41 public FieldReference(char[] source, long pos) { 42 43 token = source; 44 nameSourcePosition = pos; 45 // by default the position are the one of the field (not true for super 46 // access) 47 // sourceStart = (int) (pos >>> 32); 48 // sourceEnd = (int) (pos & 0x00000000FFFFFFFFL); 49 sourceStart = (int) pos; 50 sourceEnd = sourceStart + source.length; 51 bits |= BindingIds.FIELD; 52 53 } 54 analyseAssignment(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, boolean isCompound)55 public FlowInfo analyseAssignment(BlockScope currentScope, 56 FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, 57 boolean isCompound) { 58 59 // compound assignment extra work 60 if (isCompound) { // check the variable part is initialized if blank 61 // final 62 if (binding.isBlankFinal() && receiver.isThis() 63 && currentScope.allowBlankFinalFieldAssignment(binding) 64 && (!flowInfo.isDefinitelyAssigned(binding))) { 65 currentScope.problemReporter().uninitializedBlankFinalField( 66 binding, this); 67 // we could improve error msg here telling "cannot use compound 68 // assignment on final blank field" 69 } 70 manageSyntheticReadAccessIfNecessary(currentScope); 71 } 72 flowInfo = receiver.analyseCode(currentScope, flowContext, flowInfo, 73 !binding.isStatic()).unconditionalInits(); 74 if (assignment.expression != null) { 75 flowInfo = assignment.expression.analyseCode(currentScope, 76 flowContext, flowInfo).unconditionalInits(); 77 } 78 manageSyntheticWriteAccessIfNecessary(currentScope); 79 80 // check if assigning a final field 81 if (binding.isFinal()) { 82 // in a context where it can be assigned? 83 if (binding.isBlankFinal() && !isCompound && receiver.isThis() 84 && !(receiver instanceof QualifiedThisReference) 85 && ((receiver.bits & ParenthesizedMASK) == 0) // (this).x 86 // is 87 // forbidden 88 && currentScope.allowBlankFinalFieldAssignment(binding)) { 89 if (flowInfo.isPotentiallyAssigned(binding)) { 90 currentScope.problemReporter() 91 .duplicateInitializationOfBlankFinalField(binding, 92 this); 93 } else { 94 flowContext.recordSettingFinal(binding, this); 95 } 96 flowInfo.markAsDefinitelyAssigned(binding); 97 } else { 98 // assigning a final field outside an initializer or constructor 99 // or wrong reference 100 currentScope.problemReporter().cannotAssignToFinalField( 101 binding, this); 102 } 103 } 104 return flowInfo; 105 } 106 analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo)107 public FlowInfo analyseCode(BlockScope currentScope, 108 FlowContext flowContext, FlowInfo flowInfo) { 109 110 return analyseCode(currentScope, flowContext, flowInfo, true); 111 } 112 analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired)113 public FlowInfo analyseCode(BlockScope currentScope, 114 FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) { 115 116 receiver.analyseCode(currentScope, flowContext, flowInfo, !binding 117 .isStatic()); 118 if (valueRequired) { 119 manageSyntheticReadAccessIfNecessary(currentScope); 120 } 121 return flowInfo; 122 } 123 fieldBinding()124 public FieldBinding fieldBinding() { 125 126 return binding; 127 } 128 129 // public void generateAssignment( 130 // BlockScope currentScope, 131 // CodeStream codeStream, 132 // Assignment assignment, 133 // boolean valueRequired) { 134 // 135 // receiver.generateCode( 136 // currentScope, 137 // codeStream, 138 // !this.codegenBinding.isStatic()); 139 // assignment.expression.generateCode(currentScope, codeStream, true); 140 // fieldStore( 141 // codeStream, 142 // this.codegenBinding, 143 // syntheticWriteAccessor, 144 // valueRequired); 145 // if (valueRequired) { 146 // codeStream.generateImplicitConversion(assignment.implicitConversion); 147 // } 148 // } 149 150 /** 151 * Field reference code generation 152 * 153 * @param currentScope 154 * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope 155 * @param codeStream 156 * net.sourceforge.phpdt.internal.compiler.codegen.CodeStream 157 * @param valueRequired 158 * boolean 159 */ 160 // public void generateCode( 161 // BlockScope currentScope, 162 // CodeStream codeStream, 163 // boolean valueRequired) { 164 // 165 // int pc = codeStream.position; 166 // if (constant != NotAConstant) { 167 // if (valueRequired) { 168 // codeStream.generateConstant(constant, implicitConversion); 169 // } 170 // } else { 171 // boolean isStatic = this.codegenBinding.isStatic(); 172 // receiver.generateCode(currentScope, codeStream, !isStatic); 173 // if (valueRequired) { 174 // if (this.codegenBinding.constant == NotAConstant) { 175 // if (this.codegenBinding.declaringClass == null) { // array length 176 // codeStream.arraylength(); 177 // } else { 178 // if (syntheticReadAccessor == null) { 179 // if (isStatic) { 180 // codeStream.getstatic(this.codegenBinding); 181 // } else { 182 // codeStream.getfield(this.codegenBinding); 183 // } 184 // } else { 185 // codeStream.invokestatic(syntheticReadAccessor); 186 // } 187 // } 188 // codeStream.generateImplicitConversion(implicitConversion); 189 // } else { 190 // if (!isStatic) { 191 // codeStream.invokeObjectGetClass(); // perform null check 192 // codeStream.pop(); 193 // } 194 // codeStream.generateConstant(this.codegenBinding.constant, 195 // implicitConversion); 196 // } 197 // } else { 198 // if (!isStatic){ 199 // codeStream.invokeObjectGetClass(); // perform null check 200 // codeStream.pop(); 201 // } 202 // } 203 // } 204 // codeStream.recordPositionsFrom(pc, this.sourceStart); 205 // } 206 // 207 // public void generateCompoundAssignment( 208 // BlockScope currentScope, 209 // CodeStream codeStream, 210 // Expression expression, 211 // int operator, 212 // int assignmentImplicitConversion, 213 // boolean valueRequired) { 214 // 215 // boolean isStatic; 216 // receiver.generateCode( 217 // currentScope, 218 // codeStream, 219 // !(isStatic = this.codegenBinding.isStatic())); 220 // if (isStatic) { 221 // if (syntheticReadAccessor == null) { 222 // codeStream.getstatic(this.codegenBinding); 223 // } else { 224 // codeStream.invokestatic(syntheticReadAccessor); 225 // } 226 // } else { 227 // codeStream.dup(); 228 // if (syntheticReadAccessor == null) { 229 // codeStream.getfield(this.codegenBinding); 230 // } else { 231 // codeStream.invokestatic(syntheticReadAccessor); 232 // } 233 // } 234 // int operationTypeID; 235 // if ((operationTypeID = implicitConversion >> 4) == T_String) { 236 // codeStream.generateStringAppend(currentScope, null, expression); 237 // } else { 238 // // promote the array reference to the suitable operation type 239 // codeStream.generateImplicitConversion(implicitConversion); 240 // // generate the increment value (will by itself be promoted to the 241 // operation value) 242 // if (expression == IntLiteral.One) { // prefix operation 243 // codeStream.generateConstant(expression.constant, implicitConversion); 244 // } else { 245 // expression.generateCode(currentScope, codeStream, true); 246 // } 247 // // perform the operation 248 // codeStream.sendOperator(operator, operationTypeID); 249 // // cast the value back to the array reference type 250 // codeStream.generateImplicitConversion(assignmentImplicitConversion); 251 // } 252 // fieldStore( 253 // codeStream, 254 // this.codegenBinding, 255 // syntheticWriteAccessor, 256 // valueRequired); 257 // } 258 // 259 // public void generatePostIncrement( 260 // BlockScope currentScope, 261 // CodeStream codeStream, 262 // CompoundAssignment postIncrement, 263 // boolean valueRequired) { 264 // 265 // boolean isStatic; 266 // receiver.generateCode( 267 // currentScope, 268 // codeStream, 269 // !(isStatic = this.codegenBinding.isStatic())); 270 // if (isStatic) { 271 // if (syntheticReadAccessor == null) { 272 // codeStream.getstatic(this.codegenBinding); 273 // } else { 274 // codeStream.invokestatic(syntheticReadAccessor); 275 // } 276 // } else { 277 // codeStream.dup(); 278 // if (syntheticReadAccessor == null) { 279 // codeStream.getfield(this.codegenBinding); 280 // } else { 281 // codeStream.invokestatic(syntheticReadAccessor); 282 // } 283 // } 284 // if (valueRequired) { 285 // if (isStatic) { 286 // if ((this.codegenBinding.type == LongBinding) 287 // || (this.codegenBinding.type == DoubleBinding)) { 288 // codeStream.dup2(); 289 // } else { 290 // codeStream.dup(); 291 // } 292 // } else { // Stack: [owner][old field value] ---> [old field 293 // value][owner][old field value] 294 // if ((this.codegenBinding.type == LongBinding) 295 // || (this.codegenBinding.type == DoubleBinding)) { 296 // codeStream.dup2_x1(); 297 // } else { 298 // codeStream.dup_x1(); 299 // } 300 // } 301 // } 302 // codeStream.generateConstant( 303 // postIncrement.expression.constant, 304 // implicitConversion); 305 // codeStream.sendOperator(postIncrement.operator, 306 // this.codegenBinding.type.id); 307 // codeStream.generateImplicitConversion( 308 // postIncrement.assignmentImplicitConversion); 309 // fieldStore(codeStream, this.codegenBinding, syntheticWriteAccessor, 310 // false); 311 // } getConstantFor(FieldBinding binding, Reference reference, boolean isImplicit, Scope referenceScope)312 public static final Constant getConstantFor(FieldBinding binding, 313 Reference reference, boolean isImplicit, Scope referenceScope) { 314 315 // propagation of the constant. 316 317 // ref can be a FieldReference, a SingleNameReference or a 318 // QualifiedNameReference 319 // indexInQualification may have a value greater than zero only for 320 // QualifiednameReference 321 // if ref==null then indexInQualification==0 AND implicitReceiver == 322 // false. This case is a 323 // degenerated case where a fake reference field (null) 324 // is associted to a real FieldBinding in order 325 // to allow its constant computation using the regular path (in other 326 // words, find the fieldDeclaration 327 // and proceed to its type resolution). As implicitReceiver is false, no 328 // error reporting 329 // against ref will be used ==> no nullPointerException risk .... 330 331 // special treatment for langage-built-in field (their declaring class 332 // is null) 333 if (binding.declaringClass == null) { 334 // currently only one field "length" : the constant computation is 335 // never done 336 return NotAConstant; 337 } 338 if (!binding.isFinal()) { 339 return binding.constant = NotAConstant; 340 } 341 if (binding.constant != null) { 342 if (isImplicit 343 || (reference instanceof QualifiedNameReference && binding == ((QualifiedNameReference) reference).binding)) { 344 return binding.constant; 345 } 346 return NotAConstant; 347 } 348 349 // The field has not been yet type checked. 350 // It also means that the field is not coming from a class that 351 // has already been compiled. It can only be from a class within 352 // compilation units to process. Thus the field is NOT from a 353 // BinaryTypeBinbing 354 355 SourceTypeBinding typeBinding = (SourceTypeBinding) binding.declaringClass; 356 TypeDeclaration typeDecl = typeBinding.scope.referenceContext; 357 FieldDeclaration fieldDecl = typeDecl.declarationOf(binding); 358 359 fieldDecl.resolve(binding.isStatic() // side effect on binding 360 ? typeDecl.staticInitializerScope 361 : typeDecl.initializerScope); 362 363 if (isImplicit 364 || (reference instanceof QualifiedNameReference && binding == ((QualifiedNameReference) reference).binding)) { 365 return binding.constant; 366 } 367 return NotAConstant; 368 } 369 isSuperAccess()370 public boolean isSuperAccess() { 371 372 return receiver.isSuper(); 373 } 374 isTypeAccess()375 public boolean isTypeAccess() { 376 377 return receiver != null && receiver.isTypeReference(); 378 } 379 380 /* 381 * No need to emulate access to protected fields since not implicitly 382 * accessed 383 */ manageSyntheticReadAccessIfNecessary(BlockScope currentScope)384 public void manageSyntheticReadAccessIfNecessary(BlockScope currentScope) { 385 386 if (binding.isPrivate()) { 387 if ((currentScope.enclosingSourceType() != binding.declaringClass) 388 && (binding.constant == NotAConstant)) { 389 syntheticReadAccessor = ((SourceTypeBinding) binding.declaringClass) 390 .addSyntheticMethod(binding, true); 391 currentScope.problemReporter().needToEmulateFieldReadAccess( 392 binding, this); 393 return; 394 } 395 396 } else if (receiver instanceof QualifiedSuperReference) { // qualified 397 // super 398 399 // qualified super need emulation always 400 SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) receiver).currentCompatibleType); 401 syntheticReadAccessor = destinationType.addSyntheticMethod(binding, 402 true); 403 currentScope.problemReporter().needToEmulateFieldReadAccess( 404 binding, this); 405 return; 406 407 } else if (binding.isProtected()) { 408 409 SourceTypeBinding enclosingSourceType; 410 if (((bits & DepthMASK) != 0) 411 && binding.declaringClass.getPackage() != (enclosingSourceType = currentScope 412 .enclosingSourceType()).getPackage()) { 413 414 SourceTypeBinding currentCompatibleType = (SourceTypeBinding) enclosingSourceType 415 .enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT); 416 syntheticReadAccessor = currentCompatibleType 417 .addSyntheticMethod(binding, true); 418 currentScope.problemReporter().needToEmulateFieldReadAccess( 419 binding, this); 420 return; 421 } 422 } 423 // if the binding declaring class is not visible, need special action 424 // for runtime compatibility on 1.2 VMs : change the declaring class of 425 // the binding 426 // NOTE: from target 1.2 on, field's declaring class is touched if any 427 // different from receiver type 428 // if (binding.declaringClass != this.receiverType 429 // && !this.receiverType.isArrayType() 430 // && binding.declaringClass != null // array.length 431 // && binding.constant == NotAConstant 432 // && ((currentScope.environment().options.targetJDK >= 433 // CompilerOptions.JDK1_2 434 // && binding.declaringClass.id != T_Object) 435 // //no change for Object fields (in case there was) 436 // || !binding.declaringClass.canBeSeenBy(currentScope))) { 437 // this.codegenBinding = 438 // currentScope.enclosingSourceType().getUpdatedFieldBinding( 439 // binding, 440 // (ReferenceBinding) this.receiverType); 441 // } 442 } 443 444 /* 445 * No need to emulate access to protected fields since not implicitly 446 * accessed 447 */ manageSyntheticWriteAccessIfNecessary(BlockScope currentScope)448 public void manageSyntheticWriteAccessIfNecessary(BlockScope currentScope) { 449 450 if (binding.isPrivate()) { 451 if (currentScope.enclosingSourceType() != binding.declaringClass) { 452 syntheticWriteAccessor = ((SourceTypeBinding) binding.declaringClass) 453 .addSyntheticMethod(binding, false); 454 currentScope.problemReporter().needToEmulateFieldWriteAccess( 455 binding, this); 456 return; 457 } 458 459 } else if (receiver instanceof QualifiedSuperReference) { // qualified 460 // super 461 462 // qualified super need emulation always 463 SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) receiver).currentCompatibleType); 464 syntheticWriteAccessor = destinationType.addSyntheticMethod( 465 binding, false); 466 currentScope.problemReporter().needToEmulateFieldWriteAccess( 467 binding, this); 468 return; 469 470 } else if (binding.isProtected()) { 471 472 SourceTypeBinding enclosingSourceType; 473 if (((bits & DepthMASK) != 0) 474 && binding.declaringClass.getPackage() != (enclosingSourceType = currentScope 475 .enclosingSourceType()).getPackage()) { 476 477 SourceTypeBinding currentCompatibleType = (SourceTypeBinding) enclosingSourceType 478 .enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT); 479 syntheticWriteAccessor = currentCompatibleType 480 .addSyntheticMethod(binding, false); 481 currentScope.problemReporter().needToEmulateFieldWriteAccess( 482 binding, this); 483 return; 484 } 485 } 486 // if the binding declaring class is not visible, need special action 487 // for runtime compatibility on 1.2 VMs : change the declaring class of 488 // the binding 489 // NOTE: from target 1.2 on, field's declaring class is touched if any 490 // different from receiver type 491 // if (binding.declaringClass != this.receiverType 492 // && !this.receiverType.isArrayType() 493 // && binding.declaringClass != null // array.length 494 // && binding.constant == NotAConstant 495 // && ((currentScope.environment().options.targetJDK >= 496 // CompilerOptions.JDK1_2 497 // && binding.declaringClass.id != T_Object) 498 // //no change for Object fields (in case there was) 499 // || !binding.declaringClass.canBeSeenBy(currentScope))) { 500 // this.codegenBinding = 501 // currentScope.enclosingSourceType().getUpdatedFieldBinding( 502 // binding, 503 // (ReferenceBinding) this.receiverType); 504 // } 505 } 506 resolveType(BlockScope scope)507 public TypeBinding resolveType(BlockScope scope) { 508 509 // Answer the signature type of the field. 510 // constants are propaged when the field is final 511 // and initialized with a (compile time) constant 512 513 // regular receiver reference 514 this.receiverType = receiver.resolveType(scope); 515 if (this.receiverType == null) { 516 constant = NotAConstant; 517 return null; 518 } 519 // the case receiverType.isArrayType and token = 'length' is handled by 520 // the scope API 521 this.codegenBinding = this.binding = scope.getField(this.receiverType, 522 token, this); 523 if (!binding.isValidBinding()) { 524 constant = NotAConstant; 525 scope.problemReporter().invalidField(this, this.receiverType); 526 return null; 527 } 528 529 if (isFieldUseDeprecated(binding, scope)) 530 scope.problemReporter().deprecatedField(binding, this); 531 532 boolean isImplicitThisRcv = receiver.isImplicitThis(); 533 constant = FieldReference.getConstantFor(binding, this, 534 isImplicitThisRcv, scope); 535 if (!isImplicitThisRcv) { 536 constant = NotAConstant; 537 } 538 if (binding.isStatic()) { 539 // static field accessed through receiver? legal but unoptimal 540 // (optional warning) 541 if (!(isImplicitThisRcv || receiver.isSuper() || (receiver instanceof NameReference && (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) { 542 scope.problemReporter().unnecessaryReceiverForStaticField(this, 543 binding); 544 } 545 } 546 return this.resolvedType = binding.type; 547 } 548 setActualReceiverType(ReferenceBinding receiverType)549 public void setActualReceiverType(ReferenceBinding receiverType) { 550 // ignored 551 } 552 setDepth(int depth)553 public void setDepth(int depth) { 554 555 bits &= ~DepthMASK; // flush previous depth if any 556 if (depth > 0) { 557 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits 558 } 559 } 560 setFieldIndex(int index)561 public void setFieldIndex(int index) { 562 // ignored 563 } 564 printExpression(int indent, StringBuffer output)565 public StringBuffer printExpression(int indent, StringBuffer output) { 566 567 return receiver.printExpression(0, output).append('.').append(token); 568 } 569 toStringExpression()570 public String toStringExpression() { 571 572 return receiver.toString() + "." //$NON-NLS-1$ 573 + new String(token); 574 } 575 traverse(ASTVisitor visitor, BlockScope scope)576 public void traverse(ASTVisitor visitor, BlockScope scope) { 577 578 if (visitor.visit(this, scope)) { 579 receiver.traverse(visitor, scope); 580 } 581 visitor.endVisit(this, scope); 582 } 583 } 584