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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpeclipse.internal.compiler.ast;
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;
27 public class FieldReference extends Reference implements InvocationSite {
29 public Expression receiver;
31 public FieldBinding binding, codegenBinding;
32 public long nameSourcePosition; //(start<<32)+end
33 MethodBinding syntheticReadAccessor, syntheticWriteAccessor;
34 public TypeBinding receiverType;
36 public FieldReference(char[] source, long pos) {
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;
49 public FlowInfo analyseAssignment(
50 BlockScope currentScope,
51 FlowContext flowContext,
53 Assignment assignment,
56 // compound assignment extra work
57 if (isCompound) { // check the variable part is initialized if blank final
58 if (binding.isBlankFinal()
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"
65 manageSyntheticReadAccessIfNecessary(currentScope);
69 .analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic())
70 .unconditionalInits();
71 if (assignment.expression != null) {
75 .analyseCode(currentScope, flowContext, flowInfo)
76 .unconditionalInits();
78 manageSyntheticWriteAccessIfNecessary(currentScope);
80 // check if assigning a final field
81 if (binding.isFinal()) {
82 // in a context where it can be assigned?
83 if (binding.isBlankFinal()
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(
94 flowContext.recordSettingFinal(binding, this);
96 flowInfo.markAsDefinitelyAssigned(binding);
98 // assigning a final field outside an initializer or constructor or wrong reference
99 currentScope.problemReporter().cannotAssignToFinalField(binding, this);
105 public FlowInfo analyseCode(
106 BlockScope currentScope,
107 FlowContext flowContext,
110 return analyseCode(currentScope, flowContext, flowInfo, true);
113 public FlowInfo analyseCode(
114 BlockScope currentScope,
115 FlowContext flowContext,
117 boolean valueRequired) {
119 receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic());
121 manageSyntheticReadAccessIfNecessary(currentScope);
126 public FieldBinding fieldBinding() {
131 // public void generateAssignment(
132 // BlockScope currentScope,
133 // CodeStream codeStream,
134 // Assignment assignment,
135 // boolean valueRequired) {
137 // receiver.generateCode(
140 // !this.codegenBinding.isStatic());
141 // assignment.expression.generateCode(currentScope, codeStream, true);
144 // this.codegenBinding,
145 // syntheticWriteAccessor,
147 // if (valueRequired) {
148 // codeStream.generateImplicitConversion(assignment.implicitConversion);
153 * Field reference code generation
155 * @param currentScope net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
156 * @param codeStream net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
157 * @param valueRequired boolean
159 // public void generateCode(
160 // BlockScope currentScope,
161 // CodeStream codeStream,
162 // boolean valueRequired) {
164 // int pc = codeStream.position;
165 // if (constant != NotAConstant) {
166 // if (valueRequired) {
167 // codeStream.generateConstant(constant, implicitConversion);
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();
177 // if (syntheticReadAccessor == null) {
179 // codeStream.getstatic(this.codegenBinding);
181 // codeStream.getfield(this.codegenBinding);
184 // codeStream.invokestatic(syntheticReadAccessor);
187 // codeStream.generateImplicitConversion(implicitConversion);
190 // codeStream.invokeObjectGetClass(); // perform null check
193 // codeStream.generateConstant(this.codegenBinding.constant, implicitConversion);
197 // codeStream.invokeObjectGetClass(); // perform null check
202 // codeStream.recordPositionsFrom(pc, this.sourceStart);
205 // public void generateCompoundAssignment(
206 // BlockScope currentScope,
207 // CodeStream codeStream,
208 // Expression expression,
210 // int assignmentImplicitConversion,
211 // boolean valueRequired) {
214 // receiver.generateCode(
217 // !(isStatic = this.codegenBinding.isStatic()));
219 // if (syntheticReadAccessor == null) {
220 // codeStream.getstatic(this.codegenBinding);
222 // codeStream.invokestatic(syntheticReadAccessor);
226 // if (syntheticReadAccessor == null) {
227 // codeStream.getfield(this.codegenBinding);
229 // codeStream.invokestatic(syntheticReadAccessor);
232 // int operationTypeID;
233 // if ((operationTypeID = implicitConversion >> 4) == T_String) {
234 // codeStream.generateStringAppend(currentScope, null, expression);
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);
242 // expression.generateCode(currentScope, codeStream, true);
244 // // perform the operation
245 // codeStream.sendOperator(operator, operationTypeID);
246 // // cast the value back to the array reference type
247 // codeStream.generateImplicitConversion(assignmentImplicitConversion);
251 // this.codegenBinding,
252 // syntheticWriteAccessor,
256 // public void generatePostIncrement(
257 // BlockScope currentScope,
258 // CodeStream codeStream,
259 // CompoundAssignment postIncrement,
260 // boolean valueRequired) {
263 // receiver.generateCode(
266 // !(isStatic = this.codegenBinding.isStatic()));
268 // if (syntheticReadAccessor == null) {
269 // codeStream.getstatic(this.codegenBinding);
271 // codeStream.invokestatic(syntheticReadAccessor);
275 // if (syntheticReadAccessor == null) {
276 // codeStream.getfield(this.codegenBinding);
278 // codeStream.invokestatic(syntheticReadAccessor);
281 // if (valueRequired) {
283 // if ((this.codegenBinding.type == LongBinding)
284 // || (this.codegenBinding.type == DoubleBinding)) {
285 // codeStream.dup2();
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();
294 // codeStream.dup_x1();
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);
307 public static final Constant getConstantFor(
308 FieldBinding binding,
311 Scope referenceScope) {
313 //propagation of the constant.
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 ....
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
329 if (!binding.isFinal()) {
330 return binding.constant = NotAConstant;
332 if (binding.constant != null) {
333 if (isImplicit || (reference instanceof QualifiedNameReference
334 && binding == ((QualifiedNameReference)reference).binding)) {
335 return binding.constant;
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
345 SourceTypeBinding typeBinding = (SourceTypeBinding) binding.declaringClass;
346 TypeDeclaration typeDecl = typeBinding.scope.referenceContext;
347 FieldDeclaration fieldDecl = typeDecl.declarationOf(binding);
349 fieldDecl.resolve(binding.isStatic() //side effect on binding
350 ? typeDecl.staticInitializerScope
351 : typeDecl.initializerScope);
353 if (isImplicit || (reference instanceof QualifiedNameReference
354 && binding == ((QualifiedNameReference)reference).binding)) {
355 return binding.constant;
360 public boolean isSuperAccess() {
362 return receiver.isSuper();
365 public boolean isTypeAccess() {
367 return receiver != null && receiver.isTypeReference();
371 * No need to emulate access to protected fields since not implicitly accessed
373 public void manageSyntheticReadAccessIfNecessary(BlockScope currentScope) {
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);
384 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
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);
394 } else if (binding.isProtected()) {
396 SourceTypeBinding enclosingSourceType;
397 if (((bits & DepthMASK) != 0)
398 && binding.declaringClass.getPackage()
399 != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
401 SourceTypeBinding currentCompatibleType =
402 (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
403 (bits & DepthMASK) >> DepthSHIFT);
404 syntheticReadAccessor = currentCompatibleType.addSyntheticMethod(binding, true);
405 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
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(
423 // (ReferenceBinding) this.receiverType);
428 * No need to emulate access to protected fields since not implicitly accessed
430 public void manageSyntheticWriteAccessIfNecessary(BlockScope currentScope) {
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);
440 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
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);
450 } else if (binding.isProtected()) {
452 SourceTypeBinding enclosingSourceType;
453 if (((bits & DepthMASK) != 0)
454 && binding.declaringClass.getPackage()
455 != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
457 SourceTypeBinding currentCompatibleType =
458 (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
459 (bits & DepthMASK) >> DepthSHIFT);
460 syntheticWriteAccessor =
461 currentCompatibleType.addSyntheticMethod(binding, false);
462 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
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(
480 // (ReferenceBinding) this.receiverType);
484 public TypeBinding resolveType(BlockScope scope) {
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
490 // regular receiver reference
491 this.receiverType = receiver.resolveType(scope);
492 if (this.receiverType == null) {
493 constant = NotAConstant;
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);
505 if (isFieldUseDeprecated(binding, scope))
506 scope.problemReporter().deprecatedField(binding, this);
508 boolean isImplicitThisRcv = receiver.isImplicitThis();
509 constant = FieldReference.getConstantFor(binding, this, isImplicitThisRcv, scope);
510 if (!isImplicitThisRcv) {
511 constant = NotAConstant;
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);
522 return this.resolvedType = binding.type;
525 public void setActualReceiverType(ReferenceBinding receiverType) {
529 public void setDepth(int depth) {
531 bits &= ~DepthMASK; // flush previous depth if any
533 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
537 public void setFieldIndex(int index) {
540 public StringBuffer printExpression(int indent, StringBuffer output) {
542 return receiver.printExpression(0, output).append('.').append(token);
544 public String toStringExpression() {
546 return receiver.toString() + "." //$NON-NLS-1$
550 public void traverse(ASTVisitor visitor, BlockScope scope) {
552 if (visitor.visit(this, scope)) {
553 receiver.traverse(visitor, scope);
555 visitor.endVisit(this, scope);