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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
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.*;
20 public class FieldReference extends Reference implements InvocationSite {
22 public Expression receiver;
24 public FieldBinding binding, codegenBinding;
25 public long nameSourcePosition; //(start<<32)+end
26 MethodBinding syntheticReadAccessor, syntheticWriteAccessor;
27 public TypeBinding receiverType;
29 public FieldReference(char[] source, long pos) {
32 nameSourcePosition = pos;
33 //by default the position are the one of the field (not true for super access)
34 sourceStart = (int) (pos >>> 32);
35 sourceEnd = (int) (pos & 0x00000000FFFFFFFFL);
36 bits |= BindingIds.FIELD;
40 public FlowInfo analyseAssignment(
41 BlockScope currentScope,
42 FlowContext flowContext,
44 Assignment assignment,
47 // compound assignment extra work
48 if (isCompound) { // check the variable part is initialized if blank final
49 if (binding.isBlankFinal()
51 && currentScope.allowBlankFinalFieldAssignment(binding)
52 && (!flowInfo.isDefinitelyAssigned(binding))) {
53 currentScope.problemReporter().uninitializedBlankFinalField(binding, this);
54 // we could improve error msg here telling "cannot use compound assignment on final blank field"
56 manageSyntheticReadAccessIfNecessary(currentScope, flowInfo);
60 .analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic())
61 .unconditionalInits();
62 if (assignment.expression != null) {
66 .analyseCode(currentScope, flowContext, flowInfo)
67 .unconditionalInits();
69 manageSyntheticWriteAccessIfNecessary(currentScope, flowInfo);
71 // check if assigning a final field
72 if (binding.isFinal()) {
73 // in a context where it can be assigned?
74 if (binding.isBlankFinal()
77 && !(receiver instanceof QualifiedThisReference)
78 && ((receiver.bits & ParenthesizedMASK) == 0) // (this).x is forbidden
79 && currentScope.allowBlankFinalFieldAssignment(binding)) {
80 if (flowInfo.isPotentiallyAssigned(binding)) {
81 currentScope.problemReporter().duplicateInitializationOfBlankFinalField(
85 flowContext.recordSettingFinal(binding, this, flowInfo);
87 flowInfo.markAsDefinitelyAssigned(binding);
89 // assigning a final field outside an initializer or constructor or wrong reference
90 currentScope.problemReporter().cannotAssignToFinalField(binding, this);
96 public FlowInfo analyseCode(
97 BlockScope currentScope,
98 FlowContext flowContext,
101 return analyseCode(currentScope, flowContext, flowInfo, true);
104 public FlowInfo analyseCode(
105 BlockScope currentScope,
106 FlowContext flowContext,
108 boolean valueRequired) {
110 receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic());
112 manageSyntheticReadAccessIfNecessary(currentScope, flowInfo);
117 public FieldBinding fieldBinding() {
122 public void generateAssignment(
123 BlockScope currentScope,
124 CodeStream codeStream,
125 Assignment assignment,
126 boolean valueRequired) {
128 receiver.generateCode(
131 !this.codegenBinding.isStatic());
132 assignment.expression.generateCode(currentScope, codeStream, true);
136 syntheticWriteAccessor,
139 codeStream.generateImplicitConversion(assignment.implicitConversion);
144 * Field reference code generation
146 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
147 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
148 * @param valueRequired boolean
150 public void generateCode(
151 BlockScope currentScope,
152 CodeStream codeStream,
153 boolean valueRequired) {
155 int pc = codeStream.position;
156 if (constant != NotAConstant) {
158 codeStream.generateConstant(constant, implicitConversion);
161 boolean isStatic = this.codegenBinding.isStatic();
162 receiver.generateCode(currentScope, codeStream, !isStatic);
164 if (this.codegenBinding.constant == NotAConstant) {
165 if (this.codegenBinding.declaringClass == null) { // array length
166 codeStream.arraylength();
168 if (syntheticReadAccessor == null) {
170 codeStream.getstatic(this.codegenBinding);
172 codeStream.getfield(this.codegenBinding);
175 codeStream.invokestatic(syntheticReadAccessor);
178 codeStream.generateImplicitConversion(implicitConversion);
181 codeStream.invokeObjectGetClass(); // perform null check
184 codeStream.generateConstant(this.codegenBinding.constant, implicitConversion);
188 codeStream.invokeObjectGetClass(); // perform null check
193 codeStream.recordPositionsFrom(pc, this.sourceStart);
196 public void generateCompoundAssignment(
197 BlockScope currentScope,
198 CodeStream codeStream,
199 Expression expression,
201 int assignmentImplicitConversion,
202 boolean valueRequired) {
205 receiver.generateCode(
208 !(isStatic = this.codegenBinding.isStatic()));
210 if (syntheticReadAccessor == null) {
211 codeStream.getstatic(this.codegenBinding);
213 codeStream.invokestatic(syntheticReadAccessor);
217 if (syntheticReadAccessor == null) {
218 codeStream.getfield(this.codegenBinding);
220 codeStream.invokestatic(syntheticReadAccessor);
224 if ((operationTypeID = implicitConversion >> 4) == T_String) {
225 codeStream.generateStringAppend(currentScope, null, expression);
227 // promote the array reference to the suitable operation type
228 codeStream.generateImplicitConversion(implicitConversion);
229 // generate the increment value (will by itself be promoted to the operation value)
230 if (expression == IntLiteral.One) { // prefix operation
231 codeStream.generateConstant(expression.constant, implicitConversion);
233 expression.generateCode(currentScope, codeStream, true);
235 // perform the operation
236 codeStream.sendOperator(operator, operationTypeID);
237 // cast the value back to the array reference type
238 codeStream.generateImplicitConversion(assignmentImplicitConversion);
243 syntheticWriteAccessor,
247 public void generatePostIncrement(
248 BlockScope currentScope,
249 CodeStream codeStream,
250 CompoundAssignment postIncrement,
251 boolean valueRequired) {
254 receiver.generateCode(
257 !(isStatic = this.codegenBinding.isStatic()));
259 if (syntheticReadAccessor == null) {
260 codeStream.getstatic(this.codegenBinding);
262 codeStream.invokestatic(syntheticReadAccessor);
266 if (syntheticReadAccessor == null) {
267 codeStream.getfield(this.codegenBinding);
269 codeStream.invokestatic(syntheticReadAccessor);
274 if ((this.codegenBinding.type == LongBinding)
275 || (this.codegenBinding.type == DoubleBinding)) {
280 } else { // Stack: [owner][old field value] ---> [old field value][owner][old field value]
281 if ((this.codegenBinding.type == LongBinding)
282 || (this.codegenBinding.type == DoubleBinding)) {
283 codeStream.dup2_x1();
289 codeStream.generateConstant(
290 postIncrement.expression.constant,
292 codeStream.sendOperator(postIncrement.operator, this.codegenBinding.type.id);
293 codeStream.generateImplicitConversion(
294 postIncrement.assignmentImplicitConversion);
295 fieldStore(codeStream, this.codegenBinding, syntheticWriteAccessor, false);
298 public static final Constant getConstantFor(
299 FieldBinding binding,
302 Scope referenceScope) {
304 //propagation of the constant.
306 //ref can be a FieldReference, a SingleNameReference or a QualifiedNameReference
307 //indexInQualification may have a value greater than zero only for QualifiednameReference
308 //if ref==null then indexInQualification==0 AND implicitReceiver == false. This case is a
309 //degenerated case where a fake reference field (null)
310 //is associted to a real FieldBinding in order
311 //to allow its constant computation using the regular path (in other words, find the fieldDeclaration
312 //and proceed to its type resolution). As implicitReceiver is false, no error reporting
313 //against ref will be used ==> no nullPointerException risk ....
315 //special treatment for langage-built-in field (their declaring class is null)
316 if (binding.declaringClass == null) {
317 //currently only one field "length" : the constant computation is never done
320 if (!binding.isFinal()) {
321 return binding.constant = NotAConstant;
323 if (binding.constant != null) {
324 if (isImplicit || (reference instanceof QualifiedNameReference
325 && binding == ((QualifiedNameReference)reference).binding)) {
326 return binding.constant;
331 //The field has not been yet type checked.
332 //It also means that the field is not coming from a class that
333 //has already been compiled. It can only be from a class within
334 //compilation units to process. Thus the field is NOT from a BinaryTypeBinbing
336 SourceTypeBinding typeBinding = (SourceTypeBinding) binding.declaringClass;
337 TypeDeclaration typeDecl = typeBinding.scope.referenceContext;
338 FieldDeclaration fieldDecl = typeDecl.declarationOf(binding);
340 fieldDecl.resolve(binding.isStatic() //side effect on binding
341 ? typeDecl.staticInitializerScope
342 : typeDecl.initializerScope);
344 if (isImplicit || (reference instanceof QualifiedNameReference
345 && binding == ((QualifiedNameReference)reference).binding)) {
346 return binding.constant;
351 public boolean isSuperAccess() {
353 return receiver.isSuper();
356 public boolean isTypeAccess() {
358 return receiver != null && receiver.isTypeReference();
362 * No need to emulate access to protected fields since not implicitly accessed
364 public void manageSyntheticReadAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
366 if (!flowInfo.isReachable()) return;
367 if (binding.isPrivate()) {
368 if ((currentScope.enclosingSourceType() != binding.declaringClass)
369 && (binding.constant == NotAConstant)) {
370 syntheticReadAccessor =
371 ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, true);
372 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
376 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
378 // qualified super need emulation always
379 SourceTypeBinding destinationType =
380 (SourceTypeBinding) (((QualifiedSuperReference) receiver)
381 .currentCompatibleType);
382 syntheticReadAccessor = destinationType.addSyntheticMethod(binding, true);
383 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
386 } else if (binding.isProtected()) {
388 SourceTypeBinding enclosingSourceType;
389 if (((bits & DepthMASK) != 0)
390 && binding.declaringClass.getPackage()
391 != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
393 SourceTypeBinding currentCompatibleType =
394 (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
395 (bits & DepthMASK) >> DepthSHIFT);
396 syntheticReadAccessor = currentCompatibleType.addSyntheticMethod(binding, true);
397 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
401 // if the binding declaring class is not visible, need special action
402 // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
403 // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type
404 if (binding.declaringClass != this.receiverType
405 && !this.receiverType.isArrayType()
406 && binding.declaringClass != null // array.length
407 && binding.constant == NotAConstant
408 && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2
409 && binding.declaringClass.id != T_Object)
410 //no change for Object fields (in case there was)
411 || !binding.declaringClass.canBeSeenBy(currentScope))) {
412 this.codegenBinding =
413 currentScope.enclosingSourceType().getUpdatedFieldBinding(
415 (ReferenceBinding) this.receiverType);
420 * No need to emulate access to protected fields since not implicitly accessed
422 public void manageSyntheticWriteAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
424 if (!flowInfo.isReachable()) return;
425 if (binding.isPrivate()) {
426 if (currentScope.enclosingSourceType() != binding.declaringClass) {
427 syntheticWriteAccessor =
428 ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, false);
429 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
433 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
435 // qualified super need emulation always
436 SourceTypeBinding destinationType =
437 (SourceTypeBinding) (((QualifiedSuperReference) receiver)
438 .currentCompatibleType);
439 syntheticWriteAccessor = destinationType.addSyntheticMethod(binding, false);
440 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
443 } else if (binding.isProtected()) {
445 SourceTypeBinding enclosingSourceType;
446 if (((bits & DepthMASK) != 0)
447 && binding.declaringClass.getPackage()
448 != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
450 SourceTypeBinding currentCompatibleType =
451 (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
452 (bits & DepthMASK) >> DepthSHIFT);
453 syntheticWriteAccessor =
454 currentCompatibleType.addSyntheticMethod(binding, false);
455 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
459 // if the binding declaring class is not visible, need special action
460 // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
461 // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type
462 if (binding.declaringClass != this.receiverType
463 && !this.receiverType.isArrayType()
464 && binding.declaringClass != null // array.length
465 && binding.constant == NotAConstant
466 && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2
467 && binding.declaringClass.id != T_Object)
468 //no change for Object fields (in case there was)
469 || !binding.declaringClass.canBeSeenBy(currentScope))) {
470 this.codegenBinding =
471 currentScope.enclosingSourceType().getUpdatedFieldBinding(
473 (ReferenceBinding) this.receiverType);
477 public StringBuffer printExpression(int indent, StringBuffer output) {
479 return receiver.printExpression(0, output).append('.').append(token);
482 public TypeBinding resolveType(BlockScope scope) {
484 // Answer the signature type of the field.
485 // constants are propaged when the field is final
486 // and initialized with a (compile time) constant
488 //always ignore receiver cast, since may affect constant pool reference
489 boolean receiverCast = false;
490 if (this.receiver instanceof CastExpression) {
491 this.receiver.bits |= IgnoreNeedForCastCheckMASK; // will check later on
494 this.receiverType = receiver.resolveType(scope);
495 if (this.receiverType == null) {
496 constant = NotAConstant;
500 // due to change of declaring class with receiver type, only identity cast should be notified
501 if (((CastExpression)this.receiver).expression.resolvedType == this.receiverType) {
502 scope.problemReporter().unnecessaryCast((CastExpression)this.receiver);
505 // the case receiverType.isArrayType and token = 'length' is handled by the scope API
506 this.codegenBinding = this.binding = scope.getField(this.receiverType, token, this);
507 if (!binding.isValidBinding()) {
508 constant = NotAConstant;
509 scope.problemReporter().invalidField(this, this.receiverType);
513 if (isFieldUseDeprecated(binding, scope, (this.bits & IsStrictlyAssignedMASK) !=0)) {
514 scope.problemReporter().deprecatedField(binding, this);
516 boolean isImplicitThisRcv = receiver.isImplicitThis();
517 constant = FieldReference.getConstantFor(binding, this, isImplicitThisRcv, scope);
518 if (!isImplicitThisRcv) {
519 constant = NotAConstant;
521 if (binding.isStatic()) {
522 // static field accessed through receiver? legal but unoptimal (optional warning)
523 if (!(isImplicitThisRcv
524 || receiver.isSuper()
525 || (receiver instanceof NameReference
526 && (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) {
527 scope.problemReporter().nonStaticAccessToStaticField(this, binding);
529 if (!isImplicitThisRcv && binding.declaringClass != receiverType) {
530 scope.problemReporter().indirectAccessToStaticField(this, binding);
533 return this.resolvedType = binding.type;
536 public void setActualReceiverType(ReferenceBinding receiverType) {
540 public void setDepth(int depth) {
542 bits &= ~DepthMASK; // flush previous depth if any
544 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
548 public void setFieldIndex(int index) {
552 public void traverse(ASTVisitor visitor, BlockScope scope) {
554 if (visitor.visit(this, scope)) {
555 receiver.traverse(visitor, scope);
557 visitor.endVisit(this, scope);