A variable description (like PHPFunctionDeclaration)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / FieldReference.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.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.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.impl.*;
15 import net.sourceforge.phpdt.internal.compiler.codegen.*;
16 import net.sourceforge.phpdt.internal.compiler.flow.*;
17 import net.sourceforge.phpdt.internal.compiler.lookup.*;
18
19 public class FieldReference extends Reference implements InvocationSite {
20
21         public Expression receiver;
22         public char[] token;
23         public FieldBinding binding, codegenBinding;
24         public long nameSourcePosition; //(start<<32)+end
25         MethodBinding syntheticReadAccessor, syntheticWriteAccessor;
26         public TypeBinding receiverType;
27
28         public FieldReference(char[] source, long pos) {
29
30                 token = source;
31                 nameSourcePosition = pos;
32                 //by default the position are the one of the field (not true for super access)
33                 sourceStart = (int) (pos >>> 32);
34                 sourceEnd = (int) (pos & 0x00000000FFFFFFFFL);
35                 bits |= BindingIds.FIELD;
36
37         }
38
39         public FlowInfo analyseAssignment(
40                 BlockScope currentScope,
41                 FlowContext flowContext,
42                 FlowInfo flowInfo,
43                 Assignment assignment,
44                 boolean isCompound) {
45
46                 // compound assignment extra work
47                 if (isCompound) { // check the variable part is initialized if blank final
48                         if (binding.isFinal()
49                                 && receiver.isThis()
50                                 && currentScope.allowBlankFinalFieldAssignment(binding)
51                                 && (!flowInfo.isDefinitelyAssigned(binding))) {
52                                 currentScope.problemReporter().uninitializedBlankFinalField(binding, this);
53                                 // we could improve error msg here telling "cannot use compound assignment on final blank field"
54                         }
55                         manageSyntheticReadAccessIfNecessary(currentScope);
56                 }
57                 if (assignment.expression != null) {
58                         flowInfo =
59                                 assignment
60                                         .expression
61                                         .analyseCode(currentScope, flowContext, flowInfo)
62                                         .unconditionalInits();
63                 }
64                 flowInfo =
65                         receiver
66                                 .analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic())
67                                 .unconditionalInits();
68                 manageSyntheticWriteAccessIfNecessary(currentScope);
69
70                 // check if assigning a final field 
71                 if (binding.isFinal()) {
72                         // in a context where it can be assigned?
73                         if (receiver.isThis()
74                                 && !(receiver instanceof QualifiedThisReference)
75                                 && currentScope.allowBlankFinalFieldAssignment(binding)) {
76                                 if (flowInfo.isPotentiallyAssigned(binding)) {
77                                         currentScope.problemReporter().duplicateInitializationOfBlankFinalField(
78                                                 binding,
79                                                 this);
80                                 }
81                                 flowInfo.markAsDefinitelyAssigned(binding);
82                                 flowContext.recordSettingFinal(binding, this);
83                         } else {
84                                 // assigning a final field outside an initializer or constructor
85                                 currentScope.problemReporter().cannotAssignToFinalField(binding, this);
86                         }
87                 }
88                 return flowInfo;
89         }
90
91         public FlowInfo analyseCode(
92                 BlockScope currentScope,
93                 FlowContext flowContext,
94                 FlowInfo flowInfo) {
95
96                 return analyseCode(currentScope, flowContext, flowInfo, true);
97         }
98
99         public FlowInfo analyseCode(
100                 BlockScope currentScope,
101                 FlowContext flowContext,
102                 FlowInfo flowInfo,
103                 boolean valueRequired) {
104
105                 receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic());
106                 if (valueRequired) {
107                         manageSyntheticReadAccessIfNecessary(currentScope);
108                 }
109                 return flowInfo;
110         }
111
112         public FieldBinding fieldBinding() {
113
114                 return binding;
115         }
116
117         public void generateAssignment(
118                 BlockScope currentScope,
119                 CodeStream codeStream,
120                 Assignment assignment,
121                 boolean valueRequired) {
122
123                 receiver.generateCode(
124                         currentScope,
125                         codeStream,
126                         !this.codegenBinding.isStatic());
127                 assignment.expression.generateCode(currentScope, codeStream, true);
128                 fieldStore(
129                         codeStream,
130                         this.codegenBinding,
131                         syntheticWriteAccessor,
132                         valueRequired);
133                 if (valueRequired) {
134                         codeStream.generateImplicitConversion(assignment.implicitConversion);
135                 }
136         }
137
138         /**
139          * Field reference code generation
140          *
141          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
142          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
143          * @param valueRequired boolean
144          */
145         public void generateCode(
146                 BlockScope currentScope,
147                 CodeStream codeStream,
148                 boolean valueRequired) {
149
150                 int pc = codeStream.position;
151                 if (constant != NotAConstant) {
152                         if (valueRequired) {
153                                 codeStream.generateConstant(constant, implicitConversion);
154                         }
155                 } else {
156                         boolean isStatic = this.codegenBinding.isStatic();
157                         receiver.generateCode(
158                                 currentScope,
159                                 codeStream,
160                                 valueRequired && (!isStatic) && (this.codegenBinding.constant == NotAConstant));
161                         if (valueRequired) {
162                                 if (this.codegenBinding.constant == NotAConstant) {
163                                         if (this.codegenBinding.declaringClass == null) { // array length
164                                                 codeStream.arraylength();
165                                         } else {
166                                                 if (syntheticReadAccessor == null) {
167                                                         if (isStatic) {
168                                                                 codeStream.getstatic(this.codegenBinding);
169                                                         } else {
170                                                                 codeStream.getfield(this.codegenBinding);
171                                                         }
172                                                 } else {
173                                                         codeStream.invokestatic(syntheticReadAccessor);
174                                                 }
175                                         }
176                                         codeStream.generateImplicitConversion(implicitConversion);
177                                 } else {
178                                         codeStream.generateConstant(this.codegenBinding.constant, implicitConversion);
179                                 }
180                         }
181                 }
182                 codeStream.recordPositionsFrom(pc, this.sourceStart);
183         }
184
185         public void generateCompoundAssignment(
186                 BlockScope currentScope,
187                 CodeStream codeStream,
188                 Expression expression,
189                 int operator,
190                 int assignmentImplicitConversion,
191                 boolean valueRequired) {
192
193                 boolean isStatic;
194                 receiver.generateCode(
195                         currentScope,
196                         codeStream,
197                         !(isStatic = this.codegenBinding.isStatic()));
198                 if (isStatic) {
199                         if (syntheticReadAccessor == null) {
200                                 codeStream.getstatic(this.codegenBinding);
201                         } else {
202                                 codeStream.invokestatic(syntheticReadAccessor);
203                         }
204                 } else {
205                         codeStream.dup();
206                         if (syntheticReadAccessor == null) {
207                                 codeStream.getfield(this.codegenBinding);
208                         } else {
209                                 codeStream.invokestatic(syntheticReadAccessor);
210                         }
211                 }
212                 int operationTypeID;
213                 if ((operationTypeID = implicitConversion >> 4) == T_String) {
214                         codeStream.generateStringAppend(currentScope, null, expression);
215                 } else {
216                         // promote the array reference to the suitable operation type
217                         codeStream.generateImplicitConversion(implicitConversion);
218                         // generate the increment value (will by itself  be promoted to the operation value)
219                         if (expression == IntLiteral.One) { // prefix operation
220                                 codeStream.generateConstant(expression.constant, implicitConversion);
221                         } else {
222                                 expression.generateCode(currentScope, codeStream, true);
223                         }
224                         // perform the operation
225                         codeStream.sendOperator(operator, operationTypeID);
226                         // cast the value back to the array reference type
227                         codeStream.generateImplicitConversion(assignmentImplicitConversion);
228                 }
229                 fieldStore(
230                         codeStream,
231                         this.codegenBinding,
232                         syntheticWriteAccessor,
233                         valueRequired);
234         }
235
236         public void generatePostIncrement(
237                 BlockScope currentScope,
238                 CodeStream codeStream,
239                 CompoundAssignment postIncrement,
240                 boolean valueRequired) {
241
242                 boolean isStatic;
243                 receiver.generateCode(
244                         currentScope,
245                         codeStream,
246                         !(isStatic = this.codegenBinding.isStatic()));
247                 if (isStatic) {
248                         if (syntheticReadAccessor == null) {
249                                 codeStream.getstatic(this.codegenBinding);
250                         } else {
251                                 codeStream.invokestatic(syntheticReadAccessor);
252                         }
253                 } else {
254                         codeStream.dup();
255                         if (syntheticReadAccessor == null) {
256                                 codeStream.getfield(this.codegenBinding);
257                         } else {
258                                 codeStream.invokestatic(syntheticReadAccessor);
259                         }
260                 }
261                 if (valueRequired) {
262                         if (isStatic) {
263                                 if ((this.codegenBinding.type == LongBinding)
264                                         || (this.codegenBinding.type == DoubleBinding)) {
265                                         codeStream.dup2();
266                                 } else {
267                                         codeStream.dup();
268                                 }
269                         } else { // Stack:  [owner][old field value]  ---> [old field value][owner][old field value]
270                                 if ((this.codegenBinding.type == LongBinding)
271                                         || (this.codegenBinding.type == DoubleBinding)) {
272                                         codeStream.dup2_x1();
273                                 } else {
274                                         codeStream.dup_x1();
275                                 }
276                         }
277                 }
278                 codeStream.generateConstant(
279                         postIncrement.expression.constant,
280                         implicitConversion);
281                 codeStream.sendOperator(postIncrement.operator, this.codegenBinding.type.id);
282                 codeStream.generateImplicitConversion(
283                         postIncrement.assignmentImplicitConversion);
284                 fieldStore(codeStream, this.codegenBinding, syntheticWriteAccessor, false);
285         }
286
287         public static final Constant getConstantFor(
288                 FieldBinding binding,
289                 boolean implicitReceiver,
290                 Reference reference,
291                 Scope referenceScope,
292                 int indexInQualification) {
293
294                 //propagation of the constant.
295
296                 //ref can be a FieldReference, a SingleNameReference or a QualifiedNameReference
297                 //indexInQualification may have a value greater than zero only for QualifiednameReference
298                 //if ref==null then indexInQualification==0 AND implicitReceiver == false. This case is a 
299                 //degenerated case where a fake reference field (null) 
300                 //is associted to a real FieldBinding in order 
301                 //to allow its constant computation using the regular path (i.e. find the fieldDeclaration
302                 //and proceed to its type resolution). As implicitReceiver is false, no error reporting
303                 //against ref will be used ==> no nullPointerException risk .... 
304
305                 //special treatment for langage-built-in  field (their declaring class is null)
306                 if (binding.declaringClass == null) {
307                         //currently only one field "length" : the constant computation is never done
308                         return NotAConstant;
309                 }
310                 if (!binding.isFinal()) {
311                         return binding.constant = NotAConstant;
312                 }
313                 if (binding.constant != null) {
314                         if (indexInQualification == 0) {
315                                 return binding.constant;
316                         }
317                         //see previous comment for the (sould-always-be) valid cast
318                         QualifiedNameReference qualifiedReference = (QualifiedNameReference) reference;
319                         if (indexInQualification == (qualifiedReference.indexOfFirstFieldBinding - 1)) {
320                                 return binding.constant;
321                         }
322                         return NotAConstant;
323                 }
324
325                 //The field has not been yet type checked.
326                 //It also means that the field is not coming from a class that
327                 //has already been compiled. It can only be from a class within
328                 //compilation units to process. Thus the field is NOT from a BinaryTypeBinbing
329
330                 SourceTypeBinding typeBinding = (SourceTypeBinding) binding.declaringClass;
331                 TypeDeclaration typeDecl = typeBinding.scope.referenceContext;
332                 FieldDeclaration fieldDecl = typeDecl.declarationOf(binding);
333
334                 //what scope to use (depend on the staticness of the field binding)
335                 MethodScope fieldScope =
336                         binding.isStatic()
337                                 ? typeDecl.staticInitializerScope
338                                 : typeDecl.initializerScope;
339
340                 if (implicitReceiver) { //Determine if the ref is legal in the current class of the field
341                         //i.e. not a forward reference .... (they are allowed when the receiver is explicit ! ... Please don't ask me why !...yet another java mystery...)
342                         if (fieldScope.fieldDeclarationIndex == MethodScope.NotInFieldDecl) {
343                                 // no field is currently being analysed in typeDecl
344                                 fieldDecl.resolve(fieldScope); //side effect on binding :-) ... 
345                                 return binding.constant;
346                         }
347                         //We are re-entering the same class fields analysing
348                         if ((reference != null)
349                                 && (binding.declaringClass == referenceScope.enclosingSourceType()) // only complain for access inside same type
350                                 && (binding.id > fieldScope.fieldDeclarationIndex)) {
351                                 //forward reference. The declaration remains unresolved.
352                                 referenceScope.problemReporter().forwardReference(reference, indexInQualification, typeBinding);
353                                 return NotAConstant;
354                         }
355                         fieldDecl.resolve(fieldScope); //side effect on binding :-) ... 
356                         return binding.constant;
357                 }
358                 //the field reference is explicity. It has to be a "simple" like field reference to get the
359                 //constant propagation. For example in Packahe.Type.field1.field2 , field1 may have its
360                 //constant having a propagation where field2 is always not propagating its
361                 if (indexInQualification == 0) {
362                         fieldDecl.resolve(fieldScope); //side effect on binding :-) ... 
363                         return binding.constant;
364                 }
365                 // Side-effect on the field binding may not be propagated out for the qualified reference
366                 // unless it occurs in first place of the name sequence
367                 fieldDecl.resolve(fieldScope); //side effect on binding :-) ... 
368                 //see previous comment for the cast that should always be valid
369                 QualifiedNameReference qualifiedReference = (QualifiedNameReference) reference;
370                 if (indexInQualification == (qualifiedReference.indexOfFirstFieldBinding - 1)) {
371                         return binding.constant;
372                 } else {
373                         return NotAConstant;
374                 }
375         }
376
377         public boolean isSuperAccess() {
378
379                 return receiver.isSuper();
380         }
381
382         public boolean isTypeAccess() {
383
384                 return receiver != null && receiver.isTypeReference();
385         }
386
387         /*
388          * No need to emulate access to protected fields since not implicitly accessed
389          */
390         public void manageSyntheticReadAccessIfNecessary(BlockScope currentScope) {
391
392                 if (binding.isPrivate()) {
393                         if ((currentScope.enclosingSourceType() != binding.declaringClass)
394                                 && (binding.constant == NotAConstant)) {
395                                 syntheticReadAccessor =
396                                         ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, true);
397                                 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
398                                 return;
399                         }
400
401                 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
402
403                         // qualified super need emulation always
404                         SourceTypeBinding destinationType =
405                                 (SourceTypeBinding) (((QualifiedSuperReference) receiver)
406                                         .currentCompatibleType);
407                         syntheticReadAccessor = destinationType.addSyntheticMethod(binding, true);
408                         currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
409                         return;
410
411                 } else if (binding.isProtected()) {
412
413                         SourceTypeBinding enclosingSourceType;
414                         if (((bits & DepthMASK) != 0)
415                                 && binding.declaringClass.getPackage()
416                                         != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
417
418                                 SourceTypeBinding currentCompatibleType =
419                                         (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
420                                                 (bits & DepthMASK) >> DepthSHIFT);
421                                 syntheticReadAccessor = currentCompatibleType.addSyntheticMethod(binding, true);
422                                 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
423                                 return;
424                         }
425                 }
426                 // if the binding declaring class is not visible, need special action
427                 // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
428                 // NOTE: from 1.4 on, field's declaring class is touched if any different from receiver type
429                 if (binding.declaringClass != this.receiverType
430                         && !this.receiverType.isArrayType()
431                         && binding.declaringClass != null // array.length
432                         && binding.constant == NotAConstant
433                         && ((currentScope.environment().options.complianceLevel >= CompilerOptions.JDK1_4
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 accessed
446          */
447         public void manageSyntheticWriteAccessIfNecessary(BlockScope currentScope) {
448
449                 if (binding.isPrivate()) {
450                         if (currentScope.enclosingSourceType() != binding.declaringClass) {
451                                 syntheticWriteAccessor =
452                                         ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, false);
453                                 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
454                                 return;
455                         }
456
457                 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
458
459                         // qualified super need emulation always
460                         SourceTypeBinding destinationType =
461                                 (SourceTypeBinding) (((QualifiedSuperReference) receiver)
462                                         .currentCompatibleType);
463                         syntheticWriteAccessor = destinationType.addSyntheticMethod(binding, false);
464                         currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
465                         return;
466
467                 } else if (binding.isProtected()) {
468
469                         SourceTypeBinding enclosingSourceType;
470                         if (((bits & DepthMASK) != 0)
471                                 && binding.declaringClass.getPackage()
472                                         != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
473
474                                 SourceTypeBinding currentCompatibleType =
475                                         (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
476                                                 (bits & DepthMASK) >> DepthSHIFT);
477                                 syntheticWriteAccessor =
478                                         currentCompatibleType.addSyntheticMethod(binding, false);
479                                 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
480                                 return;
481                         }
482                 }
483                 // if the binding declaring class is not visible, need special action
484                 // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
485                 // NOTE: from 1.4 on, field's declaring class is touched if any different from receiver type
486                 if (binding.declaringClass != this.receiverType
487                         && !this.receiverType.isArrayType()
488                         && binding.declaringClass != null // array.length
489                         && binding.constant == NotAConstant
490                         && ((currentScope.environment().options.complianceLevel >= CompilerOptions.JDK1_4
491                                 && binding.declaringClass.id != T_Object)
492                         //no change for Object fields (in case there was)
493                                 || !binding.declaringClass.canBeSeenBy(currentScope))) {
494                         this.codegenBinding =
495                                 currentScope.enclosingSourceType().getUpdatedFieldBinding(
496                                         binding,
497                                         (ReferenceBinding) this.receiverType);
498                 }
499         }
500
501         public TypeBinding resolveType(BlockScope scope) {
502
503                 // Answer the signature type of the field.
504                 // constants are propaged when the field is final
505                 // and initialized with a (compile time) constant 
506
507                 // regular receiver reference 
508                 this.receiverType = receiver.resolveType(scope);
509                 if (this.receiverType == null) {
510                         constant = NotAConstant;
511                         return null;
512                 }
513                 // the case receiverType.isArrayType and token = 'length' is handled by the scope API
514                 this.codegenBinding =
515                         this.binding = scope.getField(this.receiverType, token, this);
516                 if (!binding.isValidBinding()) {
517                         constant = NotAConstant;
518                         scope.problemReporter().invalidField(this, this.receiverType);
519                         return null;
520                 }
521
522                 if (isFieldUseDeprecated(binding, scope))
523                         scope.problemReporter().deprecatedField(binding, this);
524
525                 // check for this.x in static is done in the resolution of the receiver
526                 constant =
527                         FieldReference.getConstantFor(
528                                 binding,
529                                 receiver == ThisReference.ThisImplicit,
530                                 this,
531                                 scope,
532                                 0);
533                 if (receiver != ThisReference.ThisImplicit)
534                         constant = NotAConstant;
535
536                 return binding.type;
537         }
538
539         public void setActualReceiverType(ReferenceBinding receiverType) {
540                 // ignored
541         }
542
543         public void setDepth(int depth) {
544
545                 if (depth > 0) {
546                         bits &= ~DepthMASK; // flush previous depth if any                      
547                         bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
548                 }
549         }
550
551         public void setFieldIndex(int index) {
552                 // ignored
553         }
554
555         public String toStringExpression() {
556
557                 return receiver.toString() + "." //$NON-NLS-1$
558                 + new String(token);
559         }
560
561         public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope scope) {
562
563                 if (visitor.visit(this, scope)) {
564                         receiver.traverse(visitor, scope);
565                 }
566                 visitor.endVisit(this, scope);
567         }
568 }