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