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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
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;
27 public class FieldReference extends Reference implements InvocationSite {
29 public Expression receiver;
33 public FieldBinding binding, codegenBinding;
35 public long nameSourcePosition; // (start<<32)+end
37 MethodBinding syntheticReadAccessor, syntheticWriteAccessor;
39 public TypeBinding receiverType;
41 public FieldReference(char[] source, long pos) {
44 nameSourcePosition = pos;
45 // by default the position are the one of the field (not true for super
47 // sourceStart = (int) (pos >>> 32);
48 // sourceEnd = (int) (pos & 0x00000000FFFFFFFFL);
49 sourceStart = (int) pos;
50 sourceEnd = sourceStart + source.length;
51 bits |= BindingIds.FIELD;
55 public FlowInfo analyseAssignment(BlockScope currentScope,
56 FlowContext flowContext, FlowInfo flowInfo, Assignment assignment,
59 // compound assignment extra work
60 if (isCompound) { // check the variable part is initialized if blank
62 if (binding.isBlankFinal() && receiver.isThis()
63 && currentScope.allowBlankFinalFieldAssignment(binding)
64 && (!flowInfo.isDefinitelyAssigned(binding))) {
65 currentScope.problemReporter().uninitializedBlankFinalField(
67 // we could improve error msg here telling "cannot use compound
68 // assignment on final blank field"
70 manageSyntheticReadAccessIfNecessary(currentScope);
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();
78 manageSyntheticWriteAccessIfNecessary(currentScope);
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
88 && currentScope.allowBlankFinalFieldAssignment(binding)) {
89 if (flowInfo.isPotentiallyAssigned(binding)) {
90 currentScope.problemReporter()
91 .duplicateInitializationOfBlankFinalField(binding,
94 flowContext.recordSettingFinal(binding, this);
96 flowInfo.markAsDefinitelyAssigned(binding);
98 // assigning a final field outside an initializer or constructor
100 currentScope.problemReporter().cannotAssignToFinalField(
107 public FlowInfo analyseCode(BlockScope currentScope,
108 FlowContext flowContext, FlowInfo flowInfo) {
110 return analyseCode(currentScope, flowContext, flowInfo, true);
113 public FlowInfo analyseCode(BlockScope currentScope,
114 FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) {
116 receiver.analyseCode(currentScope, flowContext, flowInfo, !binding
119 manageSyntheticReadAccessIfNecessary(currentScope);
124 public FieldBinding fieldBinding() {
129 // public void generateAssignment(
130 // BlockScope currentScope,
131 // CodeStream codeStream,
132 // Assignment assignment,
133 // boolean valueRequired) {
135 // receiver.generateCode(
138 // !this.codegenBinding.isStatic());
139 // assignment.expression.generateCode(currentScope, codeStream, true);
142 // this.codegenBinding,
143 // syntheticWriteAccessor,
145 // if (valueRequired) {
146 // codeStream.generateImplicitConversion(assignment.implicitConversion);
151 * Field reference code generation
153 * @param currentScope
154 * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
156 * net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
157 * @param valueRequired
160 // public void generateCode(
161 // BlockScope currentScope,
162 // CodeStream codeStream,
163 // boolean valueRequired) {
165 // int pc = codeStream.position;
166 // if (constant != NotAConstant) {
167 // if (valueRequired) {
168 // codeStream.generateConstant(constant, implicitConversion);
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();
178 // if (syntheticReadAccessor == null) {
180 // codeStream.getstatic(this.codegenBinding);
182 // codeStream.getfield(this.codegenBinding);
185 // codeStream.invokestatic(syntheticReadAccessor);
188 // codeStream.generateImplicitConversion(implicitConversion);
191 // codeStream.invokeObjectGetClass(); // perform null check
194 // codeStream.generateConstant(this.codegenBinding.constant,
195 // implicitConversion);
199 // codeStream.invokeObjectGetClass(); // perform null check
204 // codeStream.recordPositionsFrom(pc, this.sourceStart);
207 // public void generateCompoundAssignment(
208 // BlockScope currentScope,
209 // CodeStream codeStream,
210 // Expression expression,
212 // int assignmentImplicitConversion,
213 // boolean valueRequired) {
216 // receiver.generateCode(
219 // !(isStatic = this.codegenBinding.isStatic()));
221 // if (syntheticReadAccessor == null) {
222 // codeStream.getstatic(this.codegenBinding);
224 // codeStream.invokestatic(syntheticReadAccessor);
228 // if (syntheticReadAccessor == null) {
229 // codeStream.getfield(this.codegenBinding);
231 // codeStream.invokestatic(syntheticReadAccessor);
234 // int operationTypeID;
235 // if ((operationTypeID = implicitConversion >> 4) == T_String) {
236 // codeStream.generateStringAppend(currentScope, null, expression);
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
242 // if (expression == IntLiteral.One) { // prefix operation
243 // codeStream.generateConstant(expression.constant, implicitConversion);
245 // expression.generateCode(currentScope, codeStream, true);
247 // // perform the operation
248 // codeStream.sendOperator(operator, operationTypeID);
249 // // cast the value back to the array reference type
250 // codeStream.generateImplicitConversion(assignmentImplicitConversion);
254 // this.codegenBinding,
255 // syntheticWriteAccessor,
259 // public void generatePostIncrement(
260 // BlockScope currentScope,
261 // CodeStream codeStream,
262 // CompoundAssignment postIncrement,
263 // boolean valueRequired) {
266 // receiver.generateCode(
269 // !(isStatic = this.codegenBinding.isStatic()));
271 // if (syntheticReadAccessor == null) {
272 // codeStream.getstatic(this.codegenBinding);
274 // codeStream.invokestatic(syntheticReadAccessor);
278 // if (syntheticReadAccessor == null) {
279 // codeStream.getfield(this.codegenBinding);
281 // codeStream.invokestatic(syntheticReadAccessor);
284 // if (valueRequired) {
286 // if ((this.codegenBinding.type == LongBinding)
287 // || (this.codegenBinding.type == DoubleBinding)) {
288 // codeStream.dup2();
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();
298 // codeStream.dup_x1();
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,
312 public static final Constant getConstantFor(FieldBinding binding,
313 Reference reference, boolean isImplicit, Scope referenceScope) {
315 // propagation of the constant.
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
329 // against ref will be used ==> no nullPointerException risk ....
331 // special treatment for langage-built-in field (their declaring class
333 if (binding.declaringClass == null) {
334 // currently only one field "length" : the constant computation is
338 if (!binding.isFinal()) {
339 return binding.constant = NotAConstant;
341 if (binding.constant != null) {
343 || (reference instanceof QualifiedNameReference && binding == ((QualifiedNameReference) reference).binding)) {
344 return binding.constant;
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
355 SourceTypeBinding typeBinding = (SourceTypeBinding) binding.declaringClass;
356 TypeDeclaration typeDecl = typeBinding.scope.referenceContext;
357 FieldDeclaration fieldDecl = typeDecl.declarationOf(binding);
359 fieldDecl.resolve(binding.isStatic() // side effect on binding
360 ? typeDecl.staticInitializerScope
361 : typeDecl.initializerScope);
364 || (reference instanceof QualifiedNameReference && binding == ((QualifiedNameReference) reference).binding)) {
365 return binding.constant;
370 public boolean isSuperAccess() {
372 return receiver.isSuper();
375 public boolean isTypeAccess() {
377 return receiver != null && receiver.isTypeReference();
381 * No need to emulate access to protected fields since not implicitly
384 public void manageSyntheticReadAccessIfNecessary(BlockScope currentScope) {
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(
396 } else if (receiver instanceof QualifiedSuperReference) { // qualified
399 // qualified super need emulation always
400 SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) receiver).currentCompatibleType);
401 syntheticReadAccessor = destinationType.addSyntheticMethod(binding,
403 currentScope.problemReporter().needToEmulateFieldReadAccess(
407 } else if (binding.isProtected()) {
409 SourceTypeBinding enclosingSourceType;
410 if (((bits & DepthMASK) != 0)
411 && binding.declaringClass.getPackage() != (enclosingSourceType = currentScope
412 .enclosingSourceType()).getPackage()) {
414 SourceTypeBinding currentCompatibleType = (SourceTypeBinding) enclosingSourceType
415 .enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
416 syntheticReadAccessor = currentCompatibleType
417 .addSyntheticMethod(binding, true);
418 currentScope.problemReporter().needToEmulateFieldReadAccess(
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
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(
440 // (ReferenceBinding) this.receiverType);
445 * No need to emulate access to protected fields since not implicitly
448 public void manageSyntheticWriteAccessIfNecessary(BlockScope currentScope) {
450 if (binding.isPrivate()) {
451 if (currentScope.enclosingSourceType() != binding.declaringClass) {
452 syntheticWriteAccessor = ((SourceTypeBinding) binding.declaringClass)
453 .addSyntheticMethod(binding, false);
454 currentScope.problemReporter().needToEmulateFieldWriteAccess(
459 } else if (receiver instanceof QualifiedSuperReference) { // qualified
462 // qualified super need emulation always
463 SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) receiver).currentCompatibleType);
464 syntheticWriteAccessor = destinationType.addSyntheticMethod(
466 currentScope.problemReporter().needToEmulateFieldWriteAccess(
470 } else if (binding.isProtected()) {
472 SourceTypeBinding enclosingSourceType;
473 if (((bits & DepthMASK) != 0)
474 && binding.declaringClass.getPackage() != (enclosingSourceType = currentScope
475 .enclosingSourceType()).getPackage()) {
477 SourceTypeBinding currentCompatibleType = (SourceTypeBinding) enclosingSourceType
478 .enclosingTypeAt((bits & DepthMASK) >> DepthSHIFT);
479 syntheticWriteAccessor = currentCompatibleType
480 .addSyntheticMethod(binding, false);
481 currentScope.problemReporter().needToEmulateFieldWriteAccess(
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
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(
503 // (ReferenceBinding) this.receiverType);
507 public TypeBinding resolveType(BlockScope scope) {
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
513 // regular receiver reference
514 this.receiverType = receiver.resolveType(scope);
515 if (this.receiverType == null) {
516 constant = NotAConstant;
519 // the case receiverType.isArrayType and token = 'length' is handled by
521 this.codegenBinding = this.binding = scope.getField(this.receiverType,
523 if (!binding.isValidBinding()) {
524 constant = NotAConstant;
525 scope.problemReporter().invalidField(this, this.receiverType);
529 if (isFieldUseDeprecated(binding, scope))
530 scope.problemReporter().deprecatedField(binding, this);
532 boolean isImplicitThisRcv = receiver.isImplicitThis();
533 constant = FieldReference.getConstantFor(binding, this,
534 isImplicitThisRcv, scope);
535 if (!isImplicitThisRcv) {
536 constant = NotAConstant;
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,
546 return this.resolvedType = binding.type;
549 public void setActualReceiverType(ReferenceBinding receiverType) {
553 public void setDepth(int depth) {
555 bits &= ~DepthMASK; // flush previous depth if any
557 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
561 public void setFieldIndex(int index) {
565 public StringBuffer printExpression(int indent, StringBuffer output) {
567 return receiver.printExpression(0, output).append('.').append(token);
570 public String toStringExpression() {
572 return receiver.toString() + "." //$NON-NLS-1$
576 public void traverse(ASTVisitor visitor, BlockScope scope) {
578 if (visitor.visit(this, scope)) {
579 receiver.traverse(visitor, scope);
581 visitor.endVisit(this, scope);