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