Applying pteague's patch (re #685)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / FieldReference.java
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
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
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
107         public FlowInfo analyseCode(BlockScope currentScope,
108                         FlowContext flowContext, FlowInfo flowInfo) {
109
110                 return analyseCode(currentScope, flowContext, flowInfo, true);
111         }
112
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
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         // }
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
370         public boolean isSuperAccess() {
371
372                 return receiver.isSuper();
373         }
374
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          */
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          */
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
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
549         public void setActualReceiverType(ReferenceBinding receiverType) {
550                 // ignored
551         }
552
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
561         public void setFieldIndex(int index) {
562                 // ignored
563         }
564
565         public StringBuffer printExpression(int indent, StringBuffer output) {
566
567                 return receiver.printExpression(0, output).append('.').append(token);
568         }
569
570         public String toStringExpression() {
571
572                 return receiver.toString() + "." //$NON-NLS-1$
573                                 + new String(token);
574         }
575
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 }