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 bits |= BindingIds.FIELD;
47 public FlowInfo analyseAssignment(
48 BlockScope currentScope,
49 FlowContext flowContext,
51 Assignment assignment,
54 // compound assignment extra work
55 if (isCompound) { // check the variable part is initialized if blank final
56 if (binding.isBlankFinal()
58 && currentScope.allowBlankFinalFieldAssignment(binding)
59 && (!flowInfo.isDefinitelyAssigned(binding))) {
60 currentScope.problemReporter().uninitializedBlankFinalField(binding, this);
61 // we could improve error msg here telling "cannot use compound assignment on final blank field"
63 manageSyntheticReadAccessIfNecessary(currentScope);
67 .analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic())
68 .unconditionalInits();
69 if (assignment.expression != null) {
73 .analyseCode(currentScope, flowContext, flowInfo)
74 .unconditionalInits();
76 manageSyntheticWriteAccessIfNecessary(currentScope);
78 // check if assigning a final field
79 if (binding.isFinal()) {
80 // in a context where it can be assigned?
81 if (binding.isBlankFinal()
84 && !(receiver instanceof QualifiedThisReference)
85 && ((receiver.bits & ParenthesizedMASK) == 0) // (this).x is forbidden
86 && currentScope.allowBlankFinalFieldAssignment(binding)) {
87 if (flowInfo.isPotentiallyAssigned(binding)) {
88 currentScope.problemReporter().duplicateInitializationOfBlankFinalField(
92 flowContext.recordSettingFinal(binding, this);
94 flowInfo.markAsDefinitelyAssigned(binding);
96 // assigning a final field outside an initializer or constructor or wrong reference
97 currentScope.problemReporter().cannotAssignToFinalField(binding, this);
103 public FlowInfo analyseCode(
104 BlockScope currentScope,
105 FlowContext flowContext,
108 return analyseCode(currentScope, flowContext, flowInfo, true);
111 public FlowInfo analyseCode(
112 BlockScope currentScope,
113 FlowContext flowContext,
115 boolean valueRequired) {
117 receiver.analyseCode(currentScope, flowContext, flowInfo, !binding.isStatic());
119 manageSyntheticReadAccessIfNecessary(currentScope);
124 public FieldBinding fieldBinding() {
129 // public void generateAssignment(
130 // BlockScope currentScope,
131 // CodeStream codeStream,
132 // Assignment assignment,
133 // boolean valueRequired) {
135 // receiver.generateCode(
138 // !this.codegenBinding.isStatic());
139 // assignment.expression.generateCode(currentScope, codeStream, true);
142 // this.codegenBinding,
143 // syntheticWriteAccessor,
145 // if (valueRequired) {
146 // codeStream.generateImplicitConversion(assignment.implicitConversion);
151 * Field reference code generation
153 * @param currentScope net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
154 * @param codeStream net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
155 * @param valueRequired boolean
157 // public void generateCode(
158 // BlockScope currentScope,
159 // CodeStream codeStream,
160 // boolean valueRequired) {
162 // int pc = codeStream.position;
163 // if (constant != NotAConstant) {
164 // if (valueRequired) {
165 // codeStream.generateConstant(constant, implicitConversion);
168 // boolean isStatic = this.codegenBinding.isStatic();
169 // receiver.generateCode(currentScope, codeStream, !isStatic);
170 // if (valueRequired) {
171 // if (this.codegenBinding.constant == NotAConstant) {
172 // if (this.codegenBinding.declaringClass == null) { // array length
173 // codeStream.arraylength();
175 // if (syntheticReadAccessor == null) {
177 // codeStream.getstatic(this.codegenBinding);
179 // codeStream.getfield(this.codegenBinding);
182 // codeStream.invokestatic(syntheticReadAccessor);
185 // codeStream.generateImplicitConversion(implicitConversion);
188 // codeStream.invokeObjectGetClass(); // perform null check
191 // codeStream.generateConstant(this.codegenBinding.constant, implicitConversion);
195 // codeStream.invokeObjectGetClass(); // perform null check
200 // codeStream.recordPositionsFrom(pc, this.sourceStart);
203 // public void generateCompoundAssignment(
204 // BlockScope currentScope,
205 // CodeStream codeStream,
206 // Expression expression,
208 // int assignmentImplicitConversion,
209 // boolean valueRequired) {
212 // receiver.generateCode(
215 // !(isStatic = this.codegenBinding.isStatic()));
217 // if (syntheticReadAccessor == null) {
218 // codeStream.getstatic(this.codegenBinding);
220 // codeStream.invokestatic(syntheticReadAccessor);
224 // if (syntheticReadAccessor == null) {
225 // codeStream.getfield(this.codegenBinding);
227 // codeStream.invokestatic(syntheticReadAccessor);
230 // int operationTypeID;
231 // if ((operationTypeID = implicitConversion >> 4) == T_String) {
232 // codeStream.generateStringAppend(currentScope, null, expression);
234 // // promote the array reference to the suitable operation type
235 // codeStream.generateImplicitConversion(implicitConversion);
236 // // generate the increment value (will by itself be promoted to the operation value)
237 // if (expression == IntLiteral.One) { // prefix operation
238 // codeStream.generateConstant(expression.constant, implicitConversion);
240 // expression.generateCode(currentScope, codeStream, true);
242 // // perform the operation
243 // codeStream.sendOperator(operator, operationTypeID);
244 // // cast the value back to the array reference type
245 // codeStream.generateImplicitConversion(assignmentImplicitConversion);
249 // this.codegenBinding,
250 // syntheticWriteAccessor,
254 // public void generatePostIncrement(
255 // BlockScope currentScope,
256 // CodeStream codeStream,
257 // CompoundAssignment postIncrement,
258 // boolean valueRequired) {
261 // receiver.generateCode(
264 // !(isStatic = this.codegenBinding.isStatic()));
266 // if (syntheticReadAccessor == null) {
267 // codeStream.getstatic(this.codegenBinding);
269 // codeStream.invokestatic(syntheticReadAccessor);
273 // if (syntheticReadAccessor == null) {
274 // codeStream.getfield(this.codegenBinding);
276 // codeStream.invokestatic(syntheticReadAccessor);
279 // if (valueRequired) {
281 // if ((this.codegenBinding.type == LongBinding)
282 // || (this.codegenBinding.type == DoubleBinding)) {
283 // codeStream.dup2();
287 // } else { // Stack: [owner][old field value] ---> [old field value][owner][old field value]
288 // if ((this.codegenBinding.type == LongBinding)
289 // || (this.codegenBinding.type == DoubleBinding)) {
290 // codeStream.dup2_x1();
292 // codeStream.dup_x1();
296 // codeStream.generateConstant(
297 // postIncrement.expression.constant,
298 // implicitConversion);
299 // codeStream.sendOperator(postIncrement.operator, this.codegenBinding.type.id);
300 // codeStream.generateImplicitConversion(
301 // postIncrement.assignmentImplicitConversion);
302 // fieldStore(codeStream, this.codegenBinding, syntheticWriteAccessor, false);
305 public static final Constant getConstantFor(
306 FieldBinding binding,
309 Scope referenceScope) {
311 //propagation of the constant.
313 //ref can be a FieldReference, a SingleNameReference or a QualifiedNameReference
314 //indexInQualification may have a value greater than zero only for QualifiednameReference
315 //if ref==null then indexInQualification==0 AND implicitReceiver == false. This case is a
316 //degenerated case where a fake reference field (null)
317 //is associted to a real FieldBinding in order
318 //to allow its constant computation using the regular path (in other words, find the fieldDeclaration
319 //and proceed to its type resolution). As implicitReceiver is false, no error reporting
320 //against ref will be used ==> no nullPointerException risk ....
322 //special treatment for langage-built-in field (their declaring class is null)
323 if (binding.declaringClass == null) {
324 //currently only one field "length" : the constant computation is never done
327 if (!binding.isFinal()) {
328 return binding.constant = NotAConstant;
330 if (binding.constant != null) {
331 if (isImplicit || (reference instanceof QualifiedNameReference
332 && binding == ((QualifiedNameReference)reference).binding)) {
333 return binding.constant;
338 //The field has not been yet type checked.
339 //It also means that the field is not coming from a class that
340 //has already been compiled. It can only be from a class within
341 //compilation units to process. Thus the field is NOT from a BinaryTypeBinbing
343 SourceTypeBinding typeBinding = (SourceTypeBinding) binding.declaringClass;
344 TypeDeclaration typeDecl = typeBinding.scope.referenceContext;
345 FieldDeclaration fieldDecl = typeDecl.declarationOf(binding);
347 fieldDecl.resolve(binding.isStatic() //side effect on binding
348 ? typeDecl.staticInitializerScope
349 : typeDecl.initializerScope);
351 if (isImplicit || (reference instanceof QualifiedNameReference
352 && binding == ((QualifiedNameReference)reference).binding)) {
353 return binding.constant;
358 public boolean isSuperAccess() {
360 return receiver.isSuper();
363 public boolean isTypeAccess() {
365 return receiver != null && receiver.isTypeReference();
369 * No need to emulate access to protected fields since not implicitly accessed
371 public void manageSyntheticReadAccessIfNecessary(BlockScope currentScope) {
373 if (binding.isPrivate()) {
374 if ((currentScope.enclosingSourceType() != binding.declaringClass)
375 && (binding.constant == NotAConstant)) {
376 syntheticReadAccessor =
377 ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, true);
378 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
382 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
384 // qualified super need emulation always
385 SourceTypeBinding destinationType =
386 (SourceTypeBinding) (((QualifiedSuperReference) receiver)
387 .currentCompatibleType);
388 syntheticReadAccessor = destinationType.addSyntheticMethod(binding, true);
389 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
392 } else if (binding.isProtected()) {
394 SourceTypeBinding enclosingSourceType;
395 if (((bits & DepthMASK) != 0)
396 && binding.declaringClass.getPackage()
397 != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
399 SourceTypeBinding currentCompatibleType =
400 (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
401 (bits & DepthMASK) >> DepthSHIFT);
402 syntheticReadAccessor = currentCompatibleType.addSyntheticMethod(binding, true);
403 currentScope.problemReporter().needToEmulateFieldReadAccess(binding, this);
407 // if the binding declaring class is not visible, need special action
408 // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
409 // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type
410 // if (binding.declaringClass != this.receiverType
411 // && !this.receiverType.isArrayType()
412 // && binding.declaringClass != null // array.length
413 // && binding.constant == NotAConstant
414 // && ((currentScope.environment().options.targetJDK >= CompilerOptions.JDK1_2
415 // && binding.declaringClass.id != T_Object)
416 // //no change for Object fields (in case there was)
417 // || !binding.declaringClass.canBeSeenBy(currentScope))) {
418 // this.codegenBinding =
419 // currentScope.enclosingSourceType().getUpdatedFieldBinding(
421 // (ReferenceBinding) this.receiverType);
426 * No need to emulate access to protected fields since not implicitly accessed
428 public void manageSyntheticWriteAccessIfNecessary(BlockScope currentScope) {
430 if (binding.isPrivate()) {
431 if (currentScope.enclosingSourceType() != binding.declaringClass) {
432 syntheticWriteAccessor =
433 ((SourceTypeBinding) binding.declaringClass).addSyntheticMethod(binding, false);
434 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
438 } else if (receiver instanceof QualifiedSuperReference) { // qualified super
440 // qualified super need emulation always
441 SourceTypeBinding destinationType =
442 (SourceTypeBinding) (((QualifiedSuperReference) receiver)
443 .currentCompatibleType);
444 syntheticWriteAccessor = destinationType.addSyntheticMethod(binding, false);
445 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
448 } else if (binding.isProtected()) {
450 SourceTypeBinding enclosingSourceType;
451 if (((bits & DepthMASK) != 0)
452 && binding.declaringClass.getPackage()
453 != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) {
455 SourceTypeBinding currentCompatibleType =
456 (SourceTypeBinding) enclosingSourceType.enclosingTypeAt(
457 (bits & DepthMASK) >> DepthSHIFT);
458 syntheticWriteAccessor =
459 currentCompatibleType.addSyntheticMethod(binding, false);
460 currentScope.problemReporter().needToEmulateFieldWriteAccess(binding, this);
464 // if the binding declaring class is not visible, need special action
465 // for runtime compatibility on 1.2 VMs : change the declaring class of the binding
466 // NOTE: from target 1.2 on, field's declaring class is touched if any different from receiver type
467 // if (binding.declaringClass != this.receiverType
468 // && !this.receiverType.isArrayType()
469 // && binding.declaringClass != null // array.length
470 // && binding.constant == NotAConstant
471 // && ((currentScope.environment().options.targetJDK >= CompilerOptions.JDK1_2
472 // && binding.declaringClass.id != T_Object)
473 // //no change for Object fields (in case there was)
474 // || !binding.declaringClass.canBeSeenBy(currentScope))) {
475 // this.codegenBinding =
476 // currentScope.enclosingSourceType().getUpdatedFieldBinding(
478 // (ReferenceBinding) this.receiverType);
482 public TypeBinding resolveType(BlockScope scope) {
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
488 // regular receiver reference
489 this.receiverType = receiver.resolveType(scope);
490 if (this.receiverType == null) {
491 constant = NotAConstant;
494 // the case receiverType.isArrayType and token = 'length' is handled by the scope API
495 this.codegenBinding =
496 this.binding = scope.getField(this.receiverType, token, this);
497 if (!binding.isValidBinding()) {
498 constant = NotAConstant;
499 scope.problemReporter().invalidField(this, this.receiverType);
503 if (isFieldUseDeprecated(binding, scope))
504 scope.problemReporter().deprecatedField(binding, this);
506 boolean isImplicitThisRcv = receiver.isImplicitThis();
507 constant = FieldReference.getConstantFor(binding, this, isImplicitThisRcv, scope);
508 if (!isImplicitThisRcv) {
509 constant = NotAConstant;
511 if (binding.isStatic()) {
512 // static field accessed through receiver? legal but unoptimal (optional warning)
513 if (!(isImplicitThisRcv
514 || receiver.isSuper()
515 || (receiver instanceof NameReference
516 && (((NameReference) receiver).bits & BindingIds.TYPE) != 0))) {
517 scope.problemReporter().unnecessaryReceiverForStaticField(this, binding);
520 return this.resolvedType = binding.type;
523 public void setActualReceiverType(ReferenceBinding receiverType) {
527 public void setDepth(int depth) {
529 bits &= ~DepthMASK; // flush previous depth if any
531 bits |= (depth & 0xFF) << DepthSHIFT; // encoded on 8 bits
535 public void setFieldIndex(int index) {
538 public StringBuffer printExpression(int indent, StringBuffer output) {
540 return receiver.printExpression(0, output).append('.').append(token);
542 public String toStringExpression() {
544 return receiver.toString() + "." //$NON-NLS-1$
548 public void traverse(ASTVisitor visitor, BlockScope scope) {
550 if (visitor.visit(this, scope)) {
551 receiver.traverse(visitor, scope);
553 visitor.endVisit(this, scope);