1 /******************************************************************************* 2 * Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.compiler.ast; 12 13 import org.eclipse.jdt.internal.compiler.ASTVisitor; 14 import org.eclipse.jdt.internal.compiler.impl.*; 15 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; 16 import org.eclipse.jdt.internal.compiler.codegen.*; 17 import org.eclipse.jdt.internal.compiler.flow.*; 18 import org.eclipse.jdt.internal.compiler.lookup.*; 19 import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; 20 21 public class QualifiedNameReference extends NameReference { 22 23 public char[][] tokens; 24 public long[] sourcePositions; 25 public FieldBinding[] otherBindings, otherCodegenBindings; 26 int[] otherDepths; 27 public int indexOfFirstFieldBinding;//points (into tokens) for the first token that corresponds to first FieldBinding 28 SyntheticAccessMethodBinding syntheticWriteAccessor; 29 SyntheticAccessMethodBinding[] syntheticReadAccessors; 30 public TypeBinding genericCast; 31 public TypeBinding[] otherGenericCasts; 32 QualifiedNameReference( char[][] sources, long[] positions, int sourceStart, int sourceEnd)33 public QualifiedNameReference( 34 char[][] sources, 35 long[] positions, 36 int sourceStart, 37 int sourceEnd) { 38 super(); 39 this.tokens = sources; 40 this.sourcePositions = positions; 41 this.sourceStart = sourceStart; 42 this.sourceEnd = sourceEnd; 43 } 44 analyseAssignment( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, boolean isCompound)45 public FlowInfo analyseAssignment( 46 BlockScope currentScope, 47 FlowContext flowContext, 48 FlowInfo flowInfo, 49 Assignment assignment, 50 boolean isCompound) { 51 52 // determine the rank until which we now we do not need any actual value for the field access 53 int otherBindingsCount = otherBindings == null ? 0 : otherBindings.length; 54 boolean needValue = otherBindingsCount == 0 || !this.otherBindings[0].isStatic(); 55 FieldBinding lastFieldBinding = null; 56 switch (bits & RestrictiveFlagMASK) { 57 case FIELD : // reading a field 58 lastFieldBinding = (FieldBinding) binding; 59 if (needValue) { 60 manageSyntheticAccessIfNecessary(currentScope, lastFieldBinding, this.actualReceiverType, 0, flowInfo); 61 } // check if final blank field 62 if (lastFieldBinding.isBlankFinal() 63 && this.otherBindings != null // the last field binding is only assigned 64 && currentScope.allowBlankFinalFieldAssignment(lastFieldBinding)) { 65 if (!flowInfo.isDefinitelyAssigned(lastFieldBinding)) { 66 currentScope.problemReporter().uninitializedBlankFinalField( 67 lastFieldBinding, 68 this); 69 } 70 } 71 break; 72 case LOCAL : 73 // first binding is a local variable 74 LocalVariableBinding localBinding; 75 if (!flowInfo 76 .isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) { 77 currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); 78 } 79 if (flowInfo.isReachable()) { 80 localBinding.useFlag = LocalVariableBinding.USED; 81 } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { 82 localBinding.useFlag = LocalVariableBinding.FAKE_USED; 83 } 84 } 85 86 if (needValue) { 87 manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); 88 // only for first binding 89 } 90 // all intermediate field accesses are read accesses 91 if (otherBindings != null) { 92 for (int i = 0; i < otherBindingsCount-1; i++) { 93 lastFieldBinding = otherBindings[i]; 94 needValue = !otherBindings[i+1].isStatic(); 95 if (needValue) { 96 manageSyntheticAccessIfNecessary( 97 currentScope, 98 lastFieldBinding, 99 i == 0 100 ? ((VariableBinding)binding).type 101 : otherBindings[i-1].type, 102 i + 1, 103 flowInfo); 104 } 105 } 106 lastFieldBinding = otherBindings[otherBindingsCount-1]; 107 } 108 109 if (isCompound) { 110 if (binding == lastFieldBinding 111 && lastFieldBinding.isBlankFinal() 112 && currentScope.allowBlankFinalFieldAssignment(lastFieldBinding) 113 && (!flowInfo.isDefinitelyAssigned(lastFieldBinding))) { 114 currentScope.problemReporter().uninitializedBlankFinalField( 115 lastFieldBinding, 116 this); 117 } 118 TypeBinding lastReceiverType; 119 if (lastFieldBinding == binding){ 120 lastReceiverType = this.actualReceiverType; 121 } else if (otherBindingsCount == 1){ 122 lastReceiverType = ((VariableBinding)this.binding).type; 123 } else { 124 lastReceiverType = this.otherBindings[otherBindingsCount-2].type; 125 } 126 manageSyntheticAccessIfNecessary( 127 currentScope, 128 lastFieldBinding, 129 lastReceiverType, 130 lastFieldBinding == binding 131 ? 0 132 : otherBindingsCount, 133 flowInfo); 134 } 135 136 if (assignment.expression != null) { 137 flowInfo = 138 assignment 139 .expression 140 .analyseCode(currentScope, flowContext, flowInfo) 141 .unconditionalInits(); 142 } 143 144 // the last field access is a write access 145 if (lastFieldBinding.isFinal()) { 146 // in a context where it can be assigned? 147 if (lastFieldBinding.isBlankFinal() 148 && !isCompound 149 && currentScope.allowBlankFinalFieldAssignment(lastFieldBinding) 150 && indexOfFirstFieldBinding == 1) { 151 if (flowInfo.isPotentiallyAssigned(lastFieldBinding)) { 152 currentScope.problemReporter().duplicateInitializationOfBlankFinalField(lastFieldBinding, this); 153 } else { 154 flowContext.recordSettingFinal(lastFieldBinding, this, flowInfo); 155 } 156 flowInfo.markAsDefinitelyAssigned(lastFieldBinding); 157 } else { 158 currentScope.problemReporter().cannotAssignToFinalField(lastFieldBinding, this); 159 if (currentScope.allowBlankFinalFieldAssignment(lastFieldBinding)) { // pretend it got assigned 160 flowInfo.markAsDefinitelyAssigned(lastFieldBinding); 161 } 162 } 163 } 164 // equivalent to valuesRequired[maxOtherBindings] 165 TypeBinding lastReceiverType; 166 if (lastFieldBinding == binding){ 167 lastReceiverType = this.actualReceiverType; 168 } else if (otherBindingsCount == 1){ 169 lastReceiverType = ((VariableBinding)this.binding).type; 170 } else { 171 lastReceiverType = this.otherBindings[otherBindingsCount-2].type; 172 } 173 manageSyntheticAccessIfNecessary(currentScope, lastFieldBinding, lastReceiverType, -1 /*write-access*/, flowInfo); 174 175 return flowInfo; 176 } 177 analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo)178 public FlowInfo analyseCode( 179 BlockScope currentScope, 180 FlowContext flowContext, 181 FlowInfo flowInfo) { 182 183 return analyseCode(currentScope, flowContext, flowInfo, true); 184 } 185 analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired)186 public FlowInfo analyseCode( 187 BlockScope currentScope, 188 FlowContext flowContext, 189 FlowInfo flowInfo, 190 boolean valueRequired) { 191 192 // determine the rank until which we now we do not need any actual value for the field access 193 int otherBindingsCount = otherBindings == null ? 0 : otherBindings.length; 194 195 boolean needValue = otherBindingsCount == 0 ? valueRequired : !this.otherBindings[0].isStatic(); 196 switch (bits & RestrictiveFlagMASK) { 197 case FIELD : // reading a field 198 if (needValue) { 199 manageSyntheticAccessIfNecessary(currentScope, (FieldBinding) binding, this.actualReceiverType, 0, flowInfo); 200 } 201 // check if reading a final blank field 202 FieldBinding fieldBinding; 203 if ((fieldBinding = (FieldBinding) binding).isBlankFinal() 204 && (indexOfFirstFieldBinding == 1) 205 // was an implicit reference to the first field binding 206 && currentScope.allowBlankFinalFieldAssignment(fieldBinding) 207 && (!flowInfo.isDefinitelyAssigned(fieldBinding))) { 208 currentScope.problemReporter().uninitializedBlankFinalField(fieldBinding, this); 209 } 210 break; 211 case LOCAL : // reading a local variable 212 LocalVariableBinding localBinding; 213 if (!flowInfo 214 .isDefinitelyAssigned(localBinding = (LocalVariableBinding) binding)) { 215 currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); 216 } 217 if (flowInfo.isReachable()) { 218 localBinding.useFlag = LocalVariableBinding.USED; 219 } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { 220 localBinding.useFlag = LocalVariableBinding.FAKE_USED; 221 } 222 } 223 if (needValue) { 224 manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); 225 // only for first binding 226 } 227 if (otherBindings != null) { 228 for (int i = 0; i < otherBindingsCount; i++) { 229 needValue = i < otherBindingsCount-1 ? !otherBindings[i+1].isStatic() : valueRequired; 230 if (needValue) { 231 manageSyntheticAccessIfNecessary( 232 currentScope, 233 otherBindings[i], 234 i == 0 ? ((VariableBinding)binding).type : otherBindings[i-1].type, 235 i + 1, 236 flowInfo); 237 } 238 } 239 } 240 return flowInfo; 241 } 242 /** 243 * Check and/or redirect the field access to the delegate receiver if any 244 */ 245 public TypeBinding checkFieldAccess(BlockScope scope) { 246 // check for forward references 247 FieldBinding fieldBinding = (FieldBinding) binding; 248 MethodScope methodScope = scope.methodScope(); 249 if (methodScope.enclosingSourceType() == fieldBinding.declaringClass 250 && methodScope.lastVisibleFieldID >= 0 251 && fieldBinding.id >= methodScope.lastVisibleFieldID) { 252 if ((!fieldBinding.isStatic() || methodScope.isStatic) 253 && this.indexOfFirstFieldBinding == 1) 254 scope.problemReporter().forwardReference(this, 0, scope.enclosingSourceType()); 255 } 256 bits &= ~RestrictiveFlagMASK; // clear bits 257 bits |= FIELD; 258 return getOtherFieldBindings(scope); 259 } 260 261 /** 262 * @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding) 263 */ computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType)264 public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) { 265 if (runtimeTimeType == null || compileTimeType == null) 266 return; 267 // set the generic cast after the fact, once the type expectation is fully known (no need for strict cast) 268 FieldBinding field = null; 269 int length = this.otherBindings == null ? 0 : this.otherBindings.length; 270 if (length == 0) { 271 if (this.binding != null && this.binding.isValidBinding()) { 272 field = (FieldBinding) this.binding; 273 } 274 } else { 275 field = this.otherBindings[length-1]; 276 } 277 if (field != null) { 278 FieldBinding originalBinding = field.original(); 279 if (originalBinding != field) { 280 // extra cast needed if method return type has type variable 281 if ((originalBinding.type.tagBits & TagBits.HasTypeVariable) != 0 && runtimeTimeType.id != T_Object) { 282 setGenericCast(length,originalBinding.type.genericCast(runtimeTimeType)); 283 } 284 } 285 } 286 super.computeConversion(scope, runtimeTimeType, compileTimeType); 287 } 288 generateAssignment( BlockScope currentScope, CodeStream codeStream, Assignment assignment, boolean valueRequired)289 public void generateAssignment( 290 BlockScope currentScope, 291 CodeStream codeStream, 292 Assignment assignment, 293 boolean valueRequired) { 294 295 FieldBinding lastFieldBinding = generateReadSequence(currentScope, codeStream); 296 assignment.expression.generateCode(currentScope, codeStream, true); 297 fieldStore(codeStream, lastFieldBinding, syntheticWriteAccessor, valueRequired); 298 // equivalent to valuesRequired[maxOtherBindings] 299 if (valueRequired) { 300 codeStream.generateImplicitConversion(assignment.implicitConversion); 301 } 302 } generateCode( BlockScope currentScope, CodeStream codeStream, boolean valueRequired)303 public void generateCode( 304 BlockScope currentScope, 305 CodeStream codeStream, 306 boolean valueRequired) { 307 308 int pc = codeStream.position; 309 if (constant != NotAConstant) { 310 if (valueRequired) { 311 codeStream.generateConstant(constant, implicitConversion); 312 } 313 } else { 314 FieldBinding lastFieldBinding = generateReadSequence(currentScope, codeStream); 315 if (valueRequired) { 316 if (lastFieldBinding.declaringClass == null) { // array length 317 codeStream.arraylength(); 318 codeStream.generateImplicitConversion(implicitConversion); 319 } else { 320 if (lastFieldBinding.isConstantValue()) { 321 if (!lastFieldBinding.isStatic()){ 322 codeStream.invokeObjectGetClass(); 323 codeStream.pop(); 324 } 325 // inline the last field constant 326 codeStream.generateConstant(lastFieldBinding.constant(), implicitConversion); 327 } else { 328 SyntheticAccessMethodBinding accessor = 329 syntheticReadAccessors == null 330 ? null 331 : syntheticReadAccessors[syntheticReadAccessors.length - 1]; 332 if (accessor == null) { 333 if (lastFieldBinding.isStatic()) { 334 codeStream.getstatic(lastFieldBinding); 335 } else { 336 codeStream.getfield(lastFieldBinding); 337 } 338 } else { 339 codeStream.invokestatic(accessor); 340 } 341 codeStream.generateImplicitConversion(implicitConversion); 342 TypeBinding requiredGenericCast = getGenericCast(this.otherCodegenBindings == null ? 0 : this.otherCodegenBindings.length); 343 if (requiredGenericCast != null) codeStream.checkcast(requiredGenericCast); 344 } 345 } 346 } else { 347 if (lastFieldBinding != null && !lastFieldBinding.isStatic()){ 348 codeStream.invokeObjectGetClass(); // perform null check 349 codeStream.pop(); 350 } 351 352 } 353 } 354 codeStream.recordPositionsFrom(pc, this.sourceStart); 355 } generateCompoundAssignment( BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired)356 public void generateCompoundAssignment( 357 BlockScope currentScope, 358 CodeStream codeStream, 359 Expression expression, 360 int operator, 361 int assignmentImplicitConversion, 362 boolean valueRequired) { 363 364 FieldBinding lastFieldBinding = generateReadSequence(currentScope, codeStream); 365 SyntheticAccessMethodBinding accessor = 366 syntheticReadAccessors == null 367 ? null 368 : syntheticReadAccessors[syntheticReadAccessors.length - 1]; 369 if (lastFieldBinding.isStatic()) { 370 if (accessor == null) { 371 codeStream.getstatic(lastFieldBinding); 372 } else { 373 codeStream.invokestatic(accessor); 374 } 375 } else { 376 codeStream.dup(); 377 if (accessor == null) { 378 codeStream.getfield(lastFieldBinding); 379 } else { 380 codeStream.invokestatic(accessor); 381 } 382 } 383 // the last field access is a write access 384 // perform the actual compound operation 385 int operationTypeID; 386 if ((operationTypeID = implicitConversion >> 4) == T_String) { 387 codeStream.generateStringConcatenationAppend(currentScope, null, expression); 388 } else { 389 // promote the array reference to the suitable operation type 390 codeStream.generateImplicitConversion(implicitConversion); 391 // generate the increment value (will by itself be promoted to the operation value) 392 if (expression == IntLiteral.One) { // prefix operation 393 codeStream.generateConstant(expression.constant, implicitConversion); 394 } else { 395 expression.generateCode(currentScope, codeStream, true); 396 } 397 // perform the operation 398 codeStream.sendOperator(operator, operationTypeID); 399 // cast the value back to the array reference type 400 codeStream.generateImplicitConversion(assignmentImplicitConversion); 401 } 402 // actual assignment 403 fieldStore(codeStream, lastFieldBinding, syntheticWriteAccessor, valueRequired); 404 // equivalent to valuesRequired[maxOtherBindings] 405 } generatePostIncrement( BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired)406 public void generatePostIncrement( 407 BlockScope currentScope, 408 CodeStream codeStream, 409 CompoundAssignment postIncrement, 410 boolean valueRequired) { 411 412 FieldBinding lastFieldBinding = generateReadSequence(currentScope, codeStream); 413 SyntheticAccessMethodBinding accessor = 414 syntheticReadAccessors == null 415 ? null 416 : syntheticReadAccessors[syntheticReadAccessors.length - 1]; 417 if (lastFieldBinding.isStatic()) { 418 if (accessor == null) { 419 codeStream.getstatic(lastFieldBinding); 420 } else { 421 codeStream.invokestatic(accessor); 422 } 423 } else { 424 codeStream.dup(); 425 if (accessor == null) { 426 codeStream.getfield(lastFieldBinding); 427 } else { 428 codeStream.invokestatic(accessor); 429 } 430 } 431 // duplicate the old field value 432 if (valueRequired) { 433 if (lastFieldBinding.isStatic()) { 434 if ((lastFieldBinding.type == LongBinding) 435 || (lastFieldBinding.type == DoubleBinding)) { 436 codeStream.dup2(); 437 } else { 438 codeStream.dup(); 439 } 440 } else { // Stack: [owner][old field value] ---> [old field value][owner][old field value] 441 if ((lastFieldBinding.type == LongBinding) 442 || (lastFieldBinding.type == DoubleBinding)) { 443 codeStream.dup2_x1(); 444 } else { 445 codeStream.dup_x1(); 446 } 447 } 448 } 449 codeStream.generateConstant( 450 postIncrement.expression.constant, 451 implicitConversion); 452 codeStream.sendOperator(postIncrement.operator, lastFieldBinding.type.id); 453 codeStream.generateImplicitConversion( 454 postIncrement.assignmentImplicitConversion); 455 fieldStore(codeStream, lastFieldBinding, syntheticWriteAccessor, false); 456 } 457 /* 458 * Generate code for all bindings (local and fields) excluding the last one, which may then be generated code 459 * for a read or write access. 460 */ generateReadSequence(BlockScope currentScope, CodeStream codeStream)461 public FieldBinding generateReadSequence(BlockScope currentScope, CodeStream codeStream) { 462 463 // determine the rank until which we now we do not need any actual value for the field access 464 int otherBindingsCount = this.otherCodegenBindings == null ? 0 : otherCodegenBindings.length; 465 boolean needValue = otherBindingsCount == 0 || !this.otherBindings[0].isStatic(); 466 FieldBinding lastFieldBinding = null; 467 TypeBinding lastGenericCast = null; 468 469 switch (bits & RestrictiveFlagMASK) { 470 case FIELD : 471 lastFieldBinding = (FieldBinding) this.codegenBinding; 472 lastGenericCast = this.genericCast; 473 // if first field is actually constant, we can inline it 474 if (lastFieldBinding.isConstantValue()) { 475 break; 476 } 477 if (needValue && !lastFieldBinding.isStatic()) { 478 if ((bits & DepthMASK) != 0) { 479 ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT); 480 Object[] emulationPath = currentScope.getEmulationPath(targetType, true /*only exact match*/, false/*consider enclosing arg*/); 481 codeStream.generateOuterAccess(emulationPath, this, targetType, currentScope); 482 } else { 483 generateReceiver(codeStream); 484 } 485 } 486 break; 487 case LOCAL : // reading the first local variable 488 if (!needValue) break; // no value needed 489 LocalVariableBinding localBinding = (LocalVariableBinding) this.codegenBinding; 490 // regular local variable read 491 if (localBinding.isConstantValue()) { 492 codeStream.generateConstant(localBinding.constant(), 0); 493 // no implicit conversion 494 } else { 495 // outer local? 496 if ((bits & DepthMASK) != 0) { 497 // outer local can be reached either through a synthetic arg or a synthetic field 498 VariableBinding[] path = currentScope.getEmulationPath(localBinding); 499 codeStream.generateOuterAccess(path, this, localBinding, currentScope); 500 } else { 501 codeStream.load(localBinding); 502 } 503 } 504 } 505 506 // all intermediate field accesses are read accesses 507 // only the last field binding is a write access 508 if (this.otherCodegenBindings != null) { 509 for (int i = 0; i < otherBindingsCount; i++) { 510 FieldBinding nextField = this.otherCodegenBindings[i]; 511 TypeBinding nextGenericCast = this.otherGenericCasts == null ? null : this.otherGenericCasts[i]; 512 if (lastFieldBinding != null) { 513 needValue = !nextField.isStatic(); 514 if (needValue) { 515 MethodBinding accessor = 516 syntheticReadAccessors == null ? null : syntheticReadAccessors[i]; 517 if (accessor == null) { 518 if (lastFieldBinding.isConstantValue()) { 519 if (lastFieldBinding != this.codegenBinding && !lastFieldBinding.isStatic()) { 520 codeStream.invokeObjectGetClass(); // perform null check 521 codeStream.pop(); 522 } 523 codeStream.generateConstant(lastFieldBinding.constant(), 0); 524 } else if (lastFieldBinding.isStatic()) { 525 codeStream.getstatic(lastFieldBinding); 526 } else { 527 codeStream.getfield(lastFieldBinding); 528 } 529 } else { 530 codeStream.invokestatic(accessor); 531 } 532 if (lastGenericCast != null) codeStream.checkcast(lastGenericCast); 533 } else { 534 if (this.codegenBinding != lastFieldBinding && !lastFieldBinding.isStatic()){ 535 codeStream.invokeObjectGetClass(); // perform null check 536 codeStream.pop(); 537 } 538 } 539 } 540 lastFieldBinding = nextField; 541 lastGenericCast = nextGenericCast; 542 } 543 } 544 return lastFieldBinding; 545 } generateReceiver(CodeStream codeStream)546 public void generateReceiver(CodeStream codeStream) { 547 codeStream.aload_0(); 548 } 549 550 /** 551 * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments() 552 */ genericTypeArguments()553 public TypeBinding[] genericTypeArguments() { 554 return null; 555 } 556 557 // get the matching codegenBinding getCodegenBinding(int index)558 protected FieldBinding getCodegenBinding(int index) { 559 if (index == 0){ 560 return (FieldBinding)this.codegenBinding; 561 } else { 562 return this.otherCodegenBindings[index-1]; 563 } 564 } 565 566 // get the matching generic cast getGenericCast(int index)567 protected TypeBinding getGenericCast(int index) { 568 if (index == 0){ 569 return this.genericCast; 570 } else { 571 if (this.otherGenericCasts == null) return null; 572 return this.otherGenericCasts[index-1]; 573 } 574 } 575 getOtherFieldBindings(BlockScope scope)576 public TypeBinding getOtherFieldBindings(BlockScope scope) { 577 // At this point restrictiveFlag may ONLY have two potential value : FIELD LOCAL (i.e cast <<(VariableBinding) binding>> is valid) 578 int length = tokens.length; 579 FieldBinding field; 580 if ((bits & FIELD) != 0) { 581 field = (FieldBinding) this.binding; 582 if (!field.isStatic()) { 583 //must check for the static status.... 584 if (indexOfFirstFieldBinding > 1 //accessing to a field using a type as "receiver" is allowed only with static field 585 || scope.methodScope().isStatic) { // the field is the first token of the qualified reference.... 586 scope.problemReporter().staticFieldAccessToNonStaticVariable(this, field); 587 return null; 588 } 589 } else { 590 // indirect static reference ? 591 if (indexOfFirstFieldBinding > 1 592 && field.declaringClass != actualReceiverType) { 593 scope.problemReporter().indirectAccessToStaticField(this, field); 594 } 595 } 596 // only last field is actually a write access if any 597 if (isFieldUseDeprecated(field, scope, (this.bits & IsStrictlyAssignedMASK) !=0 && indexOfFirstFieldBinding == length)) 598 scope.problemReporter().deprecatedField(field, this); 599 } else { 600 field = null; 601 } 602 TypeBinding type = ((VariableBinding) binding).type; 603 int index = indexOfFirstFieldBinding; 604 if (index == length) { // restrictiveFlag == FIELD 605 this.constant = FieldReference.getConstantFor((FieldBinding) binding, this, false, scope); 606 return type; 607 } 608 // allocation of the fieldBindings array and its respective constants 609 int otherBindingsLength = length - index; 610 otherCodegenBindings = otherBindings = new FieldBinding[otherBindingsLength]; 611 otherDepths = new int[otherBindingsLength]; 612 613 // fill the first constant (the one of the binding) 614 this.constant = field != null 615 ? FieldReference.getConstantFor((FieldBinding) binding, this, false, scope) 616 : ((VariableBinding) binding).constant(); 617 // save first depth, since will be updated by visibility checks of other bindings 618 int firstDepth = (bits & DepthMASK) >> DepthSHIFT; 619 // iteration on each field 620 while (index < length) { 621 char[] token = tokens[index]; 622 if (type == null) 623 return null; // could not resolve type prior to this point 624 625 // set generic cast of for previous field (if any) 626 if (field != null) { 627 FieldBinding originalBinding = field.original(); 628 if (originalBinding != field) { 629 // extra cast needed if method return type has type variable 630 if ((originalBinding.type.tagBits & TagBits.HasTypeVariable) != 0 && type.id != T_Object) { 631 setGenericCast(index-1,originalBinding.type.genericCast(type)); 632 } 633 } 634 } 635 bits &= ~DepthMASK; // flush previous depth if any 636 field = scope.getField(type, token, this); 637 int place = index - indexOfFirstFieldBinding; 638 otherBindings[place] = field; 639 otherDepths[place] = (bits & DepthMASK) >> DepthSHIFT; 640 if (field.isValidBinding()) { 641 // only last field is actually a write access if any 642 if (isFieldUseDeprecated(field, scope, (this.bits & IsStrictlyAssignedMASK) !=0 && index+1 == length)) { 643 scope.problemReporter().deprecatedField(field, this); 644 } 645 Constant someConstant = FieldReference.getConstantFor(field, this, false, scope); 646 // constant propagation can only be performed as long as the previous one is a constant too. 647 if (this.constant != NotAConstant) { 648 this.constant = someConstant; 649 } 650 651 if (field.isStatic()) { 652 // static field accessed through receiver? legal but unoptimal (optional warning) 653 scope.problemReporter().nonStaticAccessToStaticField(this, field); 654 // indirect static reference ? 655 if (field.declaringClass != type) { 656 scope.problemReporter().indirectAccessToStaticField(this, field); 657 } 658 } 659 type = field.type; 660 index++; 661 } else { 662 constant = NotAConstant; //don't fill other constants slots... 663 scope.problemReporter().invalidField(this, field, index, type); 664 setDepth(firstDepth); 665 return null; 666 } 667 } 668 setDepth(firstDepth); 669 return (otherBindings[otherBindingsLength - 1]).type; 670 } manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo)671 public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { 672 if (!flowInfo.isReachable()) return; 673 //If inlinable field, forget the access emulation, the code gen will directly target it 674 if (((bits & DepthMASK) == 0) || (constant != NotAConstant)) { 675 return; 676 } 677 if ((bits & RestrictiveFlagMASK) == LOCAL) { 678 currentScope.emulateOuterAccess((LocalVariableBinding) binding); 679 } 680 } 681 /** 682 * index is <0 to denote write access emulation 683 */ manageSyntheticAccessIfNecessary( BlockScope currentScope, FieldBinding fieldBinding, TypeBinding lastReceiverType, int index, FlowInfo flowInfo)684 public void manageSyntheticAccessIfNecessary( 685 BlockScope currentScope, 686 FieldBinding fieldBinding, 687 TypeBinding lastReceiverType, 688 int index, 689 FlowInfo flowInfo) { 690 691 if (!flowInfo.isReachable()) return; 692 // index == 0 denotes the first fieldBinding, index > 0 denotes one of the 'otherBindings', index < 0 denotes a write access (to last binding) 693 if (fieldBinding.isConstantValue()) 694 return; 695 696 // if field from parameterized type got found, use the original field at codegen time 697 FieldBinding originalField = fieldBinding.original(); 698 if (originalField != fieldBinding) { 699 setCodegenBinding(index < 0 ? (this.otherBindings == null ? 0 : this.otherBindings.length) : index, originalField); 700 } 701 702 if (fieldBinding.isPrivate()) { // private access 703 FieldBinding someCodegenBinding = getCodegenBinding(index < 0 ? (this.otherBindings == null ? 0 : this.otherBindings.length) : index); 704 if (someCodegenBinding.declaringClass != currentScope.enclosingSourceType()) { 705 setSyntheticAccessor(fieldBinding, index, 706 ((SourceTypeBinding) someCodegenBinding.declaringClass).addSyntheticMethod(someCodegenBinding, index >= 0 /*read-access?*/)); 707 currentScope.problemReporter().needToEmulateFieldAccess(someCodegenBinding, this, index >= 0 /*read-access?*/); 708 return; 709 } 710 } else if (fieldBinding.isProtected()){ 711 int depth = fieldBinding == binding 712 ? (bits & DepthMASK) >> DepthSHIFT 713 : otherDepths[index < 0 ? otherDepths.length-1 : index-1]; 714 715 // implicit protected access 716 if (depth > 0 && (fieldBinding.declaringClass.getPackage() != currentScope.enclosingSourceType().getPackage())) { 717 FieldBinding someCodegenBinding = getCodegenBinding(index < 0 ? (this.otherBindings == null ? 0 : this.otherBindings.length) : index); 718 setSyntheticAccessor(fieldBinding, index, 719 ((SourceTypeBinding) currentScope.enclosingSourceType().enclosingTypeAt(depth)).addSyntheticMethod(someCodegenBinding, index >= 0 /*read-access?*/)); 720 currentScope.problemReporter().needToEmulateFieldAccess(someCodegenBinding, this, index >= 0 /*read-access?*/); 721 return; 722 } 723 } 724 // if the binding declaring class is not visible, need special action 725 // for runtime compatibility on 1.2 VMs : change the declaring class of the binding 726 // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type 727 if (fieldBinding.declaringClass != lastReceiverType 728 && !lastReceiverType.isArrayType() 729 && fieldBinding.declaringClass != null 730 && !fieldBinding.isConstantValue() 731 && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2 732 && (fieldBinding != binding || indexOfFirstFieldBinding > 1 || !fieldBinding.isStatic()) 733 && fieldBinding.declaringClass.id != T_Object) 734 || !fieldBinding.declaringClass.canBeSeenBy(currentScope))){ 735 setCodegenBinding( 736 index < 0 ? (this.otherBindings == null ? 0 : this.otherBindings.length) : index, 737 currentScope.enclosingSourceType().getUpdatedFieldBinding( 738 getCodegenBinding(index < 0 ? (this.otherBindings == null ? 0 : this.otherBindings.length) : index), 739 (ReferenceBinding)lastReceiverType.erasure())); 740 } 741 } 742 743 public StringBuffer printExpression(int indent, StringBuffer output) { 744 745 for (int i = 0; i < tokens.length; i++) { 746 if (i > 0) output.append('.'); 747 output.append(tokens[i]); 748 } 749 return output; 750 } 751 752 /** 753 * Normal field binding did not work, try to bind to a field of the delegate receiver. 754 */ 755 public TypeBinding reportError(BlockScope scope) { 756 if (binding instanceof ProblemFieldBinding) { 757 scope.problemReporter().invalidField(this, (FieldBinding) binding); 758 } else if (binding instanceof ProblemReferenceBinding) { 759 scope.problemReporter().invalidType(this, (TypeBinding) binding); 760 } else { 761 scope.problemReporter().unresolvableReference(this, binding); 762 } 763 return null; 764 } 765 public TypeBinding resolveType(BlockScope scope) { 766 // field and/or local are done before type lookups 767 // the only available value for the restrictiveFlag BEFORE 768 // the TC is Flag_Type Flag_LocalField and Flag_TypeLocalField 769 this.actualReceiverType = this.receiverType = scope.enclosingSourceType(); 770 constant = Constant.NotAConstant; 771 if ((this.codegenBinding = this.binding = scope.getBinding(tokens, bits & RestrictiveFlagMASK, this, true /*resolve*/)).isValidBinding()) { 772 switch (bits & RestrictiveFlagMASK) { 773 case VARIABLE : //============only variable=========== 774 case TYPE | VARIABLE : 775 if (binding instanceof LocalVariableBinding) { 776 if (!((LocalVariableBinding) binding).isFinal() && ((bits & DepthMASK) != 0)) 777 scope.problemReporter().cannotReferToNonFinalOuterLocal( 778 (LocalVariableBinding) binding, 779 this); 780 bits &= ~RestrictiveFlagMASK; // clear bits 781 bits |= LOCAL; 782 return this.resolvedType = getOtherFieldBindings(scope); 783 } 784 if (binding instanceof FieldBinding) { 785 // check for forward references 786 FieldBinding fieldBinding = (FieldBinding) binding; 787 MethodScope methodScope = scope.methodScope(); 788 if (methodScope.enclosingSourceType() == fieldBinding.declaringClass 789 && methodScope.lastVisibleFieldID >= 0 790 && fieldBinding.id >= methodScope.lastVisibleFieldID) { 791 if ((!fieldBinding.isStatic() || methodScope.isStatic) 792 && this.indexOfFirstFieldBinding == 1) { 793 scope.problemReporter().forwardReference(this, 0, scope.enclosingSourceType()); 794 } 795 } 796 if (!fieldBinding.isStatic() 797 && this.indexOfFirstFieldBinding == 1 798 && scope.environment().options.getSeverity(CompilerOptions.UnqualifiedFieldAccess) != ProblemSeverities.Ignore) { 799 scope.problemReporter().unqualifiedFieldAccess(this, fieldBinding); 800 } 801 bits &= ~RestrictiveFlagMASK; // clear bits 802 bits |= FIELD; 803 804 // check for deprecated receiver type 805 // deprecation check for receiver type if not first token 806 if (indexOfFirstFieldBinding > 1) { 807 if (isTypeUseDeprecated(this.actualReceiverType, scope)) 808 scope.problemReporter().deprecatedType(this.actualReceiverType, this); 809 } 810 811 return this.resolvedType = getOtherFieldBindings(scope); 812 } 813 // thus it was a type 814 bits &= ~RestrictiveFlagMASK; // clear bits 815 bits |= TYPE; 816 case TYPE : //=============only type ============== 817 TypeBinding type = (TypeBinding) binding; 818 if (isTypeUseDeprecated(type, scope)) 819 scope.problemReporter().deprecatedType(type, this); 820 return this.resolvedType = scope.convertToRawType(type); 821 } 822 } 823 //========error cases=============== 824 return this.resolvedType = this.reportError(scope); 825 } 826 827 // set the matching codegenBinding and generic cast setCodegenBinding(int index, FieldBinding someCodegenBinding)828 protected void setCodegenBinding(int index, FieldBinding someCodegenBinding) { 829 830 if (index == 0){ 831 this.codegenBinding = someCodegenBinding; 832 } else { 833 int length = this.otherBindings.length; 834 if (this.otherCodegenBindings == this.otherBindings){ 835 System.arraycopy(this.otherBindings, 0, this.otherCodegenBindings = new FieldBinding[length], 0, length); 836 } 837 this.otherCodegenBindings[index-1] = someCodegenBinding; 838 } 839 } 840 841 // set the matching codegenBinding and generic cast setGenericCast(int index, TypeBinding someGenericCast)842 protected void setGenericCast(int index, TypeBinding someGenericCast) { 843 844 if (index == 0){ 845 this.genericCast = someGenericCast; 846 } else { 847 if (this.otherGenericCasts == null) { 848 this.otherGenericCasts = new TypeBinding[this.otherBindings.length]; 849 } 850 this.otherGenericCasts[index-1] = someGenericCast; 851 } 852 } 853 854 // set the matching synthetic accessor setSyntheticAccessor(FieldBinding fieldBinding, int index, SyntheticAccessMethodBinding syntheticAccessor)855 protected void setSyntheticAccessor(FieldBinding fieldBinding, int index, SyntheticAccessMethodBinding syntheticAccessor) { 856 if (index < 0) { // write-access ? 857 syntheticWriteAccessor = syntheticAccessor; 858 } else { 859 if (syntheticReadAccessors == null) { 860 syntheticReadAccessors = new SyntheticAccessMethodBinding[otherBindings == null ? 1 : otherBindings.length + 1]; 861 } 862 syntheticReadAccessors[index] = syntheticAccessor; 863 } 864 } 865 setFieldIndex(int index)866 public void setFieldIndex(int index) { 867 this.indexOfFirstFieldBinding = index; 868 } 869 traverse(ASTVisitor visitor, BlockScope scope)870 public void traverse(ASTVisitor visitor, BlockScope scope) { 871 visitor.visit(this, scope); 872 visitor.endVisit(this, scope); 873 } unboundReferenceErrorName()874 public String unboundReferenceErrorName() { 875 return new String(tokens[0]); 876 } 877 } 878