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