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