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; // exact binding resulting from lookup
25 protected FieldBinding codegenBinding; // actual binding used for code generation (if no synthetic accessor)
26 public MethodBinding[] syntheticAccessors; // [0]=read accessor [1]=write accessor
27 public static final int READ = 0;
28 public static final int WRITE = 1;
30 public long nameSourcePosition; //(start<<32)+end
31 public TypeBinding receiverType;
32 public TypeBinding genericCast;
34 public FieldReference(char[] source, long pos) {
37 nameSourcePosition = pos;
38 //by default the position are the one of the field (not true for super access)
39 sourceStart = (int) (pos >>> 32);
40 sourceEnd = (int) (pos & 0x00000000FFFFFFFFL);
41 bits |= Binding.FIELD;
45 public FlowInfo analyseAssignment(
46 BlockScope currentScope,
47 FlowContext flowContext,
49 Assignment assignment,
52 // compound assignment extra work
53 if (isCompound) { // check the variable part is initialized if blank final
54 if (binding.isBlankFinal()
56 && currentScope.allowBlankFinalFieldAssignment(binding)
57 && (!flowInfo.isDefinitelyAssigned(binding))) {
58 currentScope.problemReporter().uninitializedBlankFinalField(binding, this);
59 // we could improve error msg here telling "cannot use compound assignment on final blank field"
61 manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/);
65 .analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic())
66 .unconditionalInits();
67 if (assignment.expression != null) {
71 .analyseCode(currentScope, flowContext, flowInfo)
72 .unconditionalInits();
74 manageSyntheticAccessIfNecessary(currentScope, flowInfo, false /*write-access*/);
76 // check if assigning a final field
77 if (binding.isFinal()) {
78 // in a context where it can be assigned?
79 if (binding.isBlankFinal()
82 && !(receiver instanceof QualifiedThisReference)
83 && ((receiver.bits & ParenthesizedMASK) == 0) // (this).x is forbidden
84 && currentScope.allowBlankFinalFieldAssignment(binding)) {
85 if (flowInfo.isPotentiallyAssigned(binding)) {
86 currentScope.problemReporter().duplicateInitializationOfBlankFinalField(
90 flowContext.recordSettingFinal(binding, this, flowInfo);
92 flowInfo.markAsDefinitelyAssigned(binding);
94 // assigning a final field outside an initializer or constructor or wrong reference
95 currentScope.problemReporter().cannotAssignToFinalField(binding, this);
101 public FlowInfo analyseCode(
102 BlockScope currentScope,
103 FlowContext flowContext,
106 return analyseCode(currentScope, flowContext, flowInfo, true);
109 public FlowInfo analyseCode(
110 BlockScope currentScope,
111 FlowContext flowContext,
113 boolean valueRequired) {
115 boolean nonStatic = !binding.isStatic();
116 receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic);
117 if (nonStatic) receiver.checkNullStatus(currentScope, flowContext, flowInfo, FlowInfo.NON_NULL);
120 manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/);
126 * @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)
128 public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) {
129 if (runtimeTimeType == null || compileTimeType == null)
131 // set the generic cast after the fact, once the type expectation is fully known (no need for strict cast)
132 if (this.binding != null && this.binding.isValidBinding()) {
133 FieldBinding originalBinding = this.binding.original();
134 if (originalBinding != this.binding) {
135 // extra cast needed if method return type has type variable
136 if ((originalBinding.type.tagBits & TagBits.HasTypeVariable) != 0 && runtimeTimeType.id != T_JavaLangObject) {
137 this.genericCast = originalBinding.type.genericCast(scope.boxing(runtimeTimeType)); // runtimeType could be base type in boxing case
141 super.computeConversion(scope, runtimeTimeType, compileTimeType);
144 public FieldBinding fieldBinding() {
149 public void generateAssignment(
150 BlockScope currentScope,
151 CodeStream codeStream,
152 Assignment assignment,
153 boolean valueRequired) {
155 receiver.generateCode(
158 !this.codegenBinding.isStatic());
159 assignment.expression.generateCode(currentScope, codeStream, true);
163 syntheticAccessors == null ? null : syntheticAccessors[WRITE],
166 codeStream.generateImplicitConversion(assignment.implicitConversion);
168 // no need for generic cast as value got dupped
172 * Field reference code generation
174 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
175 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
176 * @param valueRequired boolean
178 public void generateCode(
179 BlockScope currentScope,
180 CodeStream codeStream,
181 boolean valueRequired) {
183 int pc = codeStream.position;
184 if (constant != NotAConstant) {
186 codeStream.generateConstant(constant, implicitConversion);
189 boolean isStatic = this.codegenBinding.isStatic();
190 receiver.generateCode(currentScope, codeStream, !isStatic);
192 if (!this.codegenBinding.isConstantValue()) {
193 if (this.codegenBinding.declaringClass == null) { // array length
194 codeStream.arraylength();
196 if (syntheticAccessors == null || syntheticAccessors[READ] == null) {
198 codeStream.getstatic(this.codegenBinding);
200 codeStream.getfield(this.codegenBinding);
203 codeStream.invokestatic(syntheticAccessors[READ]);
206 if (this.genericCast != null) codeStream.checkcast(this.genericCast);
207 codeStream.generateImplicitConversion(implicitConversion);
210 codeStream.invokeObjectGetClass(); // perform null check
213 codeStream.generateConstant(this.codegenBinding.constant(), implicitConversion);
217 codeStream.invokeObjectGetClass(); // perform null check
222 codeStream.recordPositionsFrom(pc, this.sourceStart);
225 public void generateCompoundAssignment(
226 BlockScope currentScope,
227 CodeStream codeStream,
228 Expression expression,
230 int assignmentImplicitConversion,
231 boolean valueRequired) {
234 receiver.generateCode(
237 !(isStatic = this.codegenBinding.isStatic()));
239 if (syntheticAccessors == null || syntheticAccessors[READ] == null) {
240 codeStream.getstatic(this.codegenBinding);
242 codeStream.invokestatic(syntheticAccessors[READ]);
246 if (syntheticAccessors == null || syntheticAccessors[READ] == null) {
247 codeStream.getfield(this.codegenBinding);
249 codeStream.invokestatic(syntheticAccessors[READ]);
253 switch(operationTypeID = (implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) {
254 case T_JavaLangString :
255 case T_JavaLangObject :
257 codeStream.generateStringConcatenationAppend(currentScope, null, expression);
260 // promote the array reference to the suitable operation type
261 codeStream.generateImplicitConversion(implicitConversion);
262 // generate the increment value (will by itself be promoted to the operation value)
263 if (expression == IntLiteral.One) { // prefix operation
264 codeStream.generateConstant(expression.constant, implicitConversion);
266 expression.generateCode(currentScope, codeStream, true);
268 // perform the operation
269 codeStream.sendOperator(operator, operationTypeID);
270 // cast the value back to the array reference type
271 codeStream.generateImplicitConversion(assignmentImplicitConversion);
276 syntheticAccessors == null ? null : syntheticAccessors[WRITE],
278 // no need for generic cast as value got dupped
281 public void generatePostIncrement(
282 BlockScope currentScope,
283 CodeStream codeStream,
284 CompoundAssignment postIncrement,
285 boolean valueRequired) {
288 receiver.generateCode(
291 !(isStatic = this.codegenBinding.isStatic()));
293 if (syntheticAccessors == null || syntheticAccessors[READ] == null) {
294 codeStream.getstatic(this.codegenBinding);
296 codeStream.invokestatic(syntheticAccessors[READ]);
300 if (syntheticAccessors == null || syntheticAccessors[READ] == null) {
301 codeStream.getfield(this.codegenBinding);
303 codeStream.invokestatic(syntheticAccessors[READ]);
308 if ((this.codegenBinding.type == LongBinding)
309 || (this.codegenBinding.type == DoubleBinding)) {
314 } else { // Stack: [owner][old field value] ---> [old field value][owner][old field value]
315 if ((this.codegenBinding.type == LongBinding)
316 || (this.codegenBinding.type == DoubleBinding)) {
317 codeStream.dup2_x1();
323 codeStream.generateImplicitConversion(implicitConversion);
324 codeStream.generateConstant(
325 postIncrement.expression.constant,
327 codeStream.sendOperator(postIncrement.operator, this.implicitConversion & COMPILE_TYPE_MASK);
328 codeStream.generateImplicitConversion(
329 postIncrement.assignmentImplicitConversion);
330 fieldStore(codeStream, this.codegenBinding, syntheticAccessors == null ? null : syntheticAccessors[WRITE], false);
333 * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments()
335 public TypeBinding[] genericTypeArguments() {
338 public static final Constant getConstantFor(
339 FieldBinding binding,
342 Scope referenceScope) {
344 //propagation of the constant.
346 //ref can be a FieldReference, a SingleNameReference or a QualifiedNameReference
347 //indexInQualification may have a value greater than zero only for QualifiednameReference
348 //if ref==null then indexInQualification==0 AND implicitReceiver == false. This case is a
349 //degenerated case where a fake reference field (null)
350 //is associted to a real FieldBinding in order
351 //to allow its constant computation using the regular path (in other words, find the fieldDeclaration
352 //and proceed to its type resolution). As implicitReceiver is false, no error reporting
353 //against ref will be used ==> no nullPointerException risk ....
355 //special treatment for langage-built-in field (their declaring class is null)
356 if (binding.declaringClass == null) {
357 //currently only one field "length" : the constant computation is never done
360 if (!binding.isFinal()) {
361 binding.setConstant(NotAConstant);
364 Constant fieldConstant = binding.constant();
365 if (fieldConstant != null) {
366 if (isImplicit || (reference instanceof QualifiedNameReference
367 && binding == ((QualifiedNameReference)reference).binding)) {
368 return fieldConstant;
373 //The field has not been yet type checked.
374 //It also means that the field is not coming from a class that
375 //has already been compiled. It can only be from a class within
376 //compilation units to process. Thus the field is NOT from a BinaryTypeBinbing
378 FieldBinding originalField = binding.original();
379 SourceTypeBinding sourceType = (SourceTypeBinding) originalField.declaringClass;
380 TypeDeclaration typeDecl = sourceType.scope.referenceContext;
381 FieldDeclaration fieldDecl = typeDecl.declarationOf(originalField);
383 fieldDecl.resolve(originalField.isStatic() //side effect on binding
384 ? typeDecl.staticInitializerScope
385 : typeDecl.initializerScope);
387 if (isImplicit || (reference instanceof QualifiedNameReference
388 && binding == ((QualifiedNameReference)reference).binding)) {
389 return binding.constant();
394 public boolean isSuperAccess() {
396 return receiver.isSuper();
399 public boolean isTypeAccess() {
401 return receiver != null && receiver.isTypeReference();
405 * No need to emulate access to protected fields since not implicitly accessed
407 public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess) {
409 if (!flowInfo.isReachable()) return;
410 // if field from parameterized type got found, use the original field at codegen time
411 this.codegenBinding = this.binding.original();
413 if (binding.isPrivate()) {
414 if ((currentScope.enclosingSourceType() != this.codegenBinding.declaringClass) && !binding.isConstantValue()) {
415 if (syntheticAccessors == null)
416 syntheticAccessors = new MethodBinding[2];
417 syntheticAccessors[isReadAccess ? READ : WRITE] =
418 ((SourceTypeBinding) this.codegenBinding.declaringClass).addSyntheticMethod(this.codegenBinding, isReadAccess);
419 currentScope.problemReporter().needToEmulateFieldAccess(this.codegenBinding, this, isReadAccess);
423 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
425 // qualified super need emulation always
426 SourceTypeBinding destinationType =
427 (SourceTypeBinding) (((QualifiedSuperReference) receiver)
428 .currentCompatibleType);
429 if (syntheticAccessors == null)
430 syntheticAccessors = new MethodBinding[2];
431 syntheticAccessors[isReadAccess ? READ : WRITE] = destinationType.addSyntheticMethod(this.codegenBinding, isReadAccess);
432 currentScope.problemReporter().needToEmulateFieldAccess(this.codegenBinding, this, isReadAccess);
435 } else if (binding.isProtected()) {
437 SourceTypeBinding enclosingSourceType;
438 if (((bits & DepthMASK) != 0)
439 && binding.declaringClass.getPackage()
440 != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
442 SourceTypeBinding currentCompatibleType =
443 (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
444 (bits & DepthMASK) >> DepthSHIFT);
445 if (syntheticAccessors == null)
446 syntheticAccessors = new MethodBinding[2];
447 syntheticAccessors[isReadAccess ? READ : WRITE] = currentCompatibleType.addSyntheticMethod(this.codegenBinding, isReadAccess);
448 currentScope.problemReporter().needToEmulateFieldAccess(this.codegenBinding, this, isReadAccess);
452 // if the binding declaring class is not visible, need special action
453 // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
454 // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type
455 if (this.binding.declaringClass != this.receiverType
456 && !this.receiverType.isArrayType()
457 && this.binding.declaringClass != null // array.length
458 && !this.binding.isConstantValue()
459 && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2
460 && this.binding.declaringClass.id != T_JavaLangObject)
461 //no change for Object fields (in case there was)
462 || !this.codegenBinding.declaringClass.canBeSeenBy(currentScope))) {
463 this.codegenBinding =
464 currentScope.enclosingSourceType().getUpdatedFieldBinding(
466 (ReferenceBinding) this.receiverType.erasure());
471 public StringBuffer printExpression(int indent, StringBuffer output) {
473 return receiver.printExpression(0, output).append('.').append(token);
476 public TypeBinding resolveType(BlockScope scope) {
478 // Answer the signature type of the field.
479 // constants are propaged when the field is final
480 // and initialized with a (compile time) constant
482 //always ignore receiver cast, since may affect constant pool reference
483 boolean receiverCast = false;
484 if (this.receiver instanceof CastExpression) {
485 this.receiver.bits |= IgnoreNeedForCastCheckMASK; // will check later on
488 this.receiverType = receiver.resolveType(scope);
489 if (this.receiverType == null) {
490 constant = NotAConstant;
494 // due to change of declaring class with receiver type, only identity cast should be notified
495 if (((CastExpression)this.receiver).expression.resolvedType == this.receiverType) {
496 scope.problemReporter().unnecessaryCast((CastExpression)this.receiver);
499 // the case receiverType.isArrayType and token = 'length' is handled by the scope API
500 this.codegenBinding = this.binding = scope.getField(this.receiverType, token, this);
501 if (!binding.isValidBinding()) {
502 constant = NotAConstant;
503 scope.problemReporter().invalidField(this, this.receiverType);
506 this.receiver.computeConversion(scope, this.receiverType, this.receiverType);
507 if (isFieldUseDeprecated(binding, scope, (this.bits & IsStrictlyAssignedMASK) !=0)) {
508 scope.problemReporter().deprecatedField(binding, this);
510 boolean isImplicitThisRcv = receiver.isImplicitThis();
511 constant = FieldReference.getConstantFor(binding, this, isImplicitThisRcv, scope);
512 if (!isImplicitThisRcv) {
513 constant = NotAConstant;
515 if (binding.isStatic()) {
516 // static field accessed through receiver? legal but unoptimal (optional warning)
517 if (!(isImplicitThisRcv
518 || (receiver instanceof NameReference
519 && (((NameReference) receiver).bits & Binding.TYPE) != 0))) {
520 scope.problemReporter().nonStaticAccessToStaticField(this, binding);
522 if (!isImplicitThisRcv && binding.declaringClass != receiverType) {
523 scope.problemReporter().indirectAccessToStaticField(this, binding);
526 return this.resolvedType = binding.type;
529 public void setActualReceiverType(ReferenceBinding receiverType) {
533 public void setDepth(int depth) {
535 bits &= ~DepthMASK; // flush previous depth if any
537 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
541 public void setFieldIndex(int index) {
545 public void traverse(ASTVisitor visitor, BlockScope scope) {
547 if (visitor.visit(this, scope)) {
548 receiver.traverse(visitor, scope);
550 visitor.endVisit(this, scope);