Makefile fixup
[org.ibex.tool.git] / repo / org.ibex.tool / src / org / eclipse / jdt / internal / compiler / ast / FieldReference.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.compiler.ast;
12
13 import org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.impl.*;
15 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
16 import org.eclipse.jdt.internal.compiler.codegen.*;
17 import org.eclipse.jdt.internal.compiler.flow.*;
18 import org.eclipse.jdt.internal.compiler.lookup.*;
19
20 public class FieldReference extends Reference implements InvocationSite {
21
22         public Expression receiver;
23         public char[] token;
24         public FieldBinding binding, codegenBinding;
25         public long nameSourcePosition; //(start<<32)+end
26         MethodBinding syntheticReadAccessor, syntheticWriteAccessor;
27         public TypeBinding receiverType;
28
29         public FieldReference(char[] source, long pos) {
30
31                 token = source;
32                 nameSourcePosition = pos;
33                 //by default the position are the one of the field (not true for super access)
34                 sourceStart = (int) (pos >>> 32);
35                 sourceEnd = (int) (pos & 0x00000000FFFFFFFFL);
36                 bits |= BindingIds.FIELD;
37
38         }
39
40         public FlowInfo analyseAssignment(
41                 BlockScope currentScope,
42                 FlowContext flowContext,
43                 FlowInfo flowInfo,
44                 Assignment assignment,
45                 boolean isCompound) {
46
47                 // compound assignment extra work
48                 if (isCompound) { // check the variable part is initialized if blank final
49                         if (binding.isBlankFinal()
50                                 && receiver.isThis()
51                                 && currentScope.allowBlankFinalFieldAssignment(binding)
52                                 && (!flowInfo.isDefinitelyAssigned(binding))) {
53                                 currentScope.problemReporter().uninitializedBlankFinalField(binding, this);
54                                 // we could improve error msg here telling "cannot use compound assignment on final blank field"
55                         }
56                         manageSyntheticReadAccessIfNecessary(currentScope, flowInfo);
57                 }
58                 flowInfo =
59                         receiver
60                                 .analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic())
61                                 .unconditionalInits();
62                 if (assignment.expression != null) {
63                         flowInfo =
64                                 assignment
65                                         .expression
66                                         .analyseCode(currentScope, flowContext, flowInfo)
67                                         .unconditionalInits();
68                 }
69                 manageSyntheticWriteAccessIfNecessary(currentScope, flowInfo);
70
71                 // check if assigning a final field 
72                 if (binding.isFinal()) {
73                         // in a context where it can be assigned?
74                         if (binding.isBlankFinal()
75                                 && !isCompound
76                                 && receiver.isThis()
77                                 && !(receiver instanceof QualifiedThisReference)
78                                 && ((receiver.bits & ParenthesizedMASK) == 0) // (this).x is forbidden
79                                 && currentScope.allowBlankFinalFieldAssignment(binding)) {
80                                 if (flowInfo.isPotentiallyAssigned(binding)) {
81                                         currentScope.problemReporter().duplicateInitializationOfBlankFinalField(
82                                                 binding,
83                                                 this);
84                                 } else {
85                                         flowContext.recordSettingFinal(binding, this, flowInfo);
86                                 }
87                                 flowInfo.markAsDefinitelyAssigned(binding);
88                         } else {
89                                 // assigning a final field outside an initializer or constructor or wrong reference
90                                 currentScope.problemReporter().cannotAssignToFinalField(binding, this);
91                         }
92                 }
93                 return flowInfo;
94         }
95
96         public FlowInfo analyseCode(
97                 BlockScope currentScope,
98                 FlowContext flowContext,
99                 FlowInfo flowInfo) {
100
101                 return analyseCode(currentScope, flowContext, flowInfo, true);
102         }
103
104         public FlowInfo analyseCode(
105                 BlockScope currentScope,
106                 FlowContext flowContext,
107                 FlowInfo flowInfo,
108                 boolean valueRequired) {
109
110                 receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic());
111                 if (valueRequired) {
112                         manageSyntheticReadAccessIfNecessary(currentScope, flowInfo);
113                 }
114                 return flowInfo;
115         }
116
117         public FieldBinding fieldBinding() {
118
119                 return binding;
120         }
121
122         public void generateAssignment(
123                 BlockScope currentScope,
124                 CodeStream codeStream,
125                 Assignment assignment,
126                 boolean valueRequired) {
127
128                 receiver.generateCode(
129                         currentScope,
130                         codeStream,
131                         !this.codegenBinding.isStatic());
132                 assignment.expression.generateCode(currentScope, codeStream, true);
133                 fieldStore(
134                         codeStream,
135                         this.codegenBinding,
136                         syntheticWriteAccessor,
137                         valueRequired);
138                 if (valueRequired) {
139                         codeStream.generateImplicitConversion(assignment.implicitConversion);
140                 }
141         }
142
143         /**
144          * Field reference code generation
145          *
146          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
147          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
148          * @param valueRequired boolean
149          */
150         public void generateCode(
151                 BlockScope currentScope,
152                 CodeStream codeStream,
153                 boolean valueRequired) {
154
155                 int pc = codeStream.position;
156                 if (constant != NotAConstant) {
157                         if (valueRequired) {
158                                 codeStream.generateConstant(constant, implicitConversion);
159                         }
160                 } else {
161                         boolean isStatic = this.codegenBinding.isStatic();
162                         receiver.generateCode(currentScope, codeStream, !isStatic);
163                         if (valueRequired) {
164                                 if (this.codegenBinding.constant == NotAConstant) {
165                                         if (this.codegenBinding.declaringClass == null) { // array length
166                                                 codeStream.arraylength();
167                                         } else {
168                                                 if (syntheticReadAccessor == null) {
169                                                         if (isStatic) {
170                                                                 codeStream.getstatic(this.codegenBinding);
171                                                         } else {
172                                                                 codeStream.getfield(this.codegenBinding);
173                                                         }
174                                                 } else {
175                                                         codeStream.invokestatic(syntheticReadAccessor);
176                                                 }
177                                         }
178                                         codeStream.generateImplicitConversion(implicitConversion);
179                                 } else {
180                                         if (!isStatic) {
181                                                 codeStream.invokeObjectGetClass(); // perform null check
182                                                 codeStream.pop();
183                                         }
184                                         codeStream.generateConstant(this.codegenBinding.constant, implicitConversion);
185                                 }
186                         } else {
187                                 if (!isStatic){
188                                         codeStream.invokeObjectGetClass(); // perform null check
189                                         codeStream.pop();
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                 Reference reference,
301                 boolean isImplicit,
302                 Scope referenceScope) {
303
304                 //propagation of the constant.
305
306                 //ref can be a FieldReference, a SingleNameReference or a QualifiedNameReference
307                 //indexInQualification may have a value greater than zero only for QualifiednameReference
308                 //if ref==null then indexInQualification==0 AND implicitReceiver == false. This case is a 
309                 //degenerated case where a fake reference field (null) 
310                 //is associted to a real FieldBinding in order 
311                 //to allow its constant computation using the regular path (in other words, find the fieldDeclaration
312                 //and proceed to its type resolution). As implicitReceiver is false, no error reporting
313                 //against ref will be used ==> no nullPointerException risk .... 
314
315                 //special treatment for langage-built-in  field (their declaring class is null)
316                 if (binding.declaringClass == null) {
317                         //currently only one field "length" : the constant computation is never done
318                         return NotAConstant;
319                 }
320                 if (!binding.isFinal()) {
321                         return binding.constant = NotAConstant;
322                 }
323                 if (binding.constant != null) {
324                         if (isImplicit || (reference instanceof QualifiedNameReference
325                                         && binding == ((QualifiedNameReference)reference).binding)) {
326                                 return binding.constant;
327                         }
328                         return NotAConstant;
329                 }
330
331                 //The field has not been yet type checked.
332                 //It also means that the field is not coming from a class that
333                 //has already been compiled. It can only be from a class within
334                 //compilation units to process. Thus the field is NOT from a BinaryTypeBinbing
335
336                 SourceTypeBinding typeBinding = (SourceTypeBinding) binding.declaringClass;
337                 TypeDeclaration typeDecl = typeBinding.scope.referenceContext;
338                 FieldDeclaration fieldDecl = typeDecl.declarationOf(binding);
339
340                 fieldDecl.resolve(binding.isStatic() //side effect on binding 
341                                 ? typeDecl.staticInitializerScope
342                                 : typeDecl.initializerScope); 
343
344                 if (isImplicit || (reference instanceof QualifiedNameReference
345                                 && binding == ((QualifiedNameReference)reference).binding)) {
346                         return binding.constant;
347                 }
348                 return NotAConstant;
349         }
350
351         public boolean isSuperAccess() {
352
353                 return receiver.isSuper();
354         }
355
356         public boolean isTypeAccess() {
357
358                 return receiver != null && receiver.isTypeReference();
359         }
360
361         /*
362          * No need to emulate access to protected fields since not implicitly accessed
363          */
364         public void manageSyntheticReadAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
365
366                 if (!flowInfo.isReachable()) return;
367                 if (binding.isPrivate()) {
368                         if ((currentScope.enclosingSourceType() != binding.declaringClass)
369                                 && (binding.constant == NotAConstant)) {
370                                 syntheticReadAccessor =
371                                         ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, true);
372                                 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
373                                 return;
374                         }
375
376                 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
377
378                         // qualified super need emulation always
379                         SourceTypeBinding destinationType =
380                                 (SourceTypeBinding) (((QualifiedSuperReference) receiver)
381                                         .currentCompatibleType);
382                         syntheticReadAccessor = destinationType.addSyntheticMethod(binding, true);
383                         currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
384                         return;
385
386                 } else if (binding.isProtected()) {
387
388                         SourceTypeBinding enclosingSourceType;
389                         if (((bits & DepthMASK) != 0)
390                                 && binding.declaringClass.getPackage()
391                                         != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
392
393                                 SourceTypeBinding currentCompatibleType =
394                                         (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
395                                                 (bits & DepthMASK) >> DepthSHIFT);
396                                 syntheticReadAccessor = currentCompatibleType.addSyntheticMethod(binding, true);
397                                 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
398                                 return;
399                         }
400                 }
401                 // if the binding declaring class is not visible, need special action
402                 // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
403                 // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type
404                 if (binding.declaringClass != this.receiverType
405                         && !this.receiverType.isArrayType()
406                         && binding.declaringClass != null // array.length
407                         && binding.constant == NotAConstant
408                         && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2
409                                 && binding.declaringClass.id != T_Object)
410                         //no change for Object fields (in case there was)
411                                 || !binding.declaringClass.canBeSeenBy(currentScope))) {
412                         this.codegenBinding =
413                                 currentScope.enclosingSourceType().getUpdatedFieldBinding(
414                                         binding,
415                                         (ReferenceBinding) this.receiverType);
416                 }
417         }
418
419         /*
420          * No need to emulate access to protected fields since not implicitly accessed
421          */
422         public void manageSyntheticWriteAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
423
424                 if (!flowInfo.isReachable()) return;
425                 if (binding.isPrivate()) {
426                         if (currentScope.enclosingSourceType() != binding.declaringClass) {
427                                 syntheticWriteAccessor =
428                                         ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, false);
429                                 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
430                                 return;
431                         }
432
433                 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
434
435                         // qualified super need emulation always
436                         SourceTypeBinding destinationType =
437                                 (SourceTypeBinding) (((QualifiedSuperReference) receiver)
438                                         .currentCompatibleType);
439                         syntheticWriteAccessor = destinationType.addSyntheticMethod(binding, false);
440                         currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
441                         return;
442
443                 } else if (binding.isProtected()) {
444
445                         SourceTypeBinding enclosingSourceType;
446                         if (((bits & DepthMASK) != 0)
447                                 && binding.declaringClass.getPackage()
448                                         != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
449
450                                 SourceTypeBinding currentCompatibleType =
451                                         (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
452                                                 (bits & DepthMASK) >> DepthSHIFT);
453                                 syntheticWriteAccessor =
454                                         currentCompatibleType.addSyntheticMethod(binding, false);
455                                 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
456                                 return;
457                         }
458                 }
459                 // if the binding declaring class is not visible, need special action
460                 // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
461                 // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type
462                 if (binding.declaringClass != this.receiverType
463                         && !this.receiverType.isArrayType()
464                         && binding.declaringClass != null // array.length
465                         && binding.constant == NotAConstant
466                         && ((currentScope.environment().options.targetJDK >= ClassFileConstants.JDK1_2
467                                 && binding.declaringClass.id != T_Object)
468                         //no change for Object fields (in case there was)
469                                 || !binding.declaringClass.canBeSeenBy(currentScope))) {
470                         this.codegenBinding =
471                                 currentScope.enclosingSourceType().getUpdatedFieldBinding(
472                                         binding,
473                                         (ReferenceBinding) this.receiverType);
474                 }
475         }
476
477         public StringBuffer printExpression(int indent, StringBuffer output) {
478
479                 return receiver.printExpression(0, output).append('.').append(token);
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                 //always ignore receiver cast, since may affect constant pool reference
489                 boolean receiverCast = false;
490                 if (this.receiver instanceof CastExpression) {
491                         this.receiver.bits |= IgnoreNeedForCastCheckMASK; // will check later on
492                         receiverCast = true;
493                 }
494                 this.receiverType = receiver.resolveType(scope);
495                 if (this.receiverType == null) {
496                         constant = NotAConstant;
497                         return null;
498                 }
499                 if (receiverCast) {
500                          // due to change of declaring class with receiver type, only identity cast should be notified
501                         if (((CastExpression)this.receiver).expression.resolvedType == this.receiverType) { 
502                                                 scope.problemReporter().unnecessaryCast((CastExpression)this.receiver);         
503                         }
504                 }               
505                 // the case receiverType.isArrayType and token = 'length' is handled by the scope API
506                 this.codegenBinding = this.binding = scope.getField(this.receiverType, token, this);
507                 if (!binding.isValidBinding()) {
508                         constant = NotAConstant;
509                         scope.problemReporter().invalidField(this, this.receiverType);
510                         return null;
511                 }
512
513                 if (isFieldUseDeprecated(binding, scope, (this.bits & IsStrictlyAssignedMASK) !=0)) {
514                         scope.problemReporter().deprecatedField(binding, this);
515                 }
516                 boolean isImplicitThisRcv = receiver.isImplicitThis();
517                 constant = FieldReference.getConstantFor(binding, this, isImplicitThisRcv, scope);
518                 if (!isImplicitThisRcv) {
519                         constant = NotAConstant;
520                 }
521                 if (binding.isStatic()) {
522                         // static field accessed through receiver? legal but unoptimal (optional warning)
523                         if (!(isImplicitThisRcv
524                                         || receiver.isSuper()
525                                         || (receiver instanceof NameReference 
526                                                 && (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) {
527                                 scope.problemReporter().nonStaticAccessToStaticField(this, binding);
528                         }
529                         if (!isImplicitThisRcv && binding.declaringClass != receiverType) {
530                                 scope.problemReporter().indirectAccessToStaticField(this, binding);
531                         }
532                 }
533                 return this.resolvedType = binding.type;
534         }
535
536         public void setActualReceiverType(ReferenceBinding receiverType) {
537                 // ignored
538         }
539
540         public void setDepth(int depth) {
541
542                 bits &= ~DepthMASK; // flush previous depth if any                      
543                 if (depth > 0) {
544                         bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
545                 }
546         }
547
548         public void setFieldIndex(int index) {
549                 // ignored
550         }
551
552         public void traverse(ASTVisitor visitor, BlockScope scope) {
553
554                 if (visitor.visit(this, scope)) {
555                         receiver.traverse(visitor, scope);
556                 }
557                 visitor.endVisit(this, scope);
558         }
559 }