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