gt: 						if (!needValue) codeStream.pop();
context: /*******************************************************************************
   * Copyright (c) 2000, 2013 IBM Corporation and others.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *     IBM Corporation - initial API and implementation
   *     Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
   *     							bug 185682 - Increment/decrement operators mark local variables as read
   *								bug 186342 - [compiler][null] Using annotations for null checking
   *								bug 365519 - editorial cleanup after bug 186342 and bug 365387
   *								bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
   *								bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
   *								Bug 414380 - [compiler][internal] QualifiedNameReference#indexOfFirstFieldBinding does not point to the first field
   *     Jesper S Moller - Contributions for
   *								bug 382721 - [1.8][compiler] Effectively final variables needs special treatment
   *								bug 331649 - [compiler][null] consider null annotations for fields
   *								bug 383368 - [compiler][null] syntactic null analysis for field references
   *								bug 402993 - [null] Follow up of bug 401088: Missing warning about redundant null check
   *     Jesper S Moller <jesper@selskabet.org> - Contributions for
   *								bug 378674 - "The method can be declared as static" is wrong
   *******************************************************************************/
  package org.eclipse.jdt.internal.compiler.ast;
  
  import org.eclipse.jdt.core.compiler.CharOperation;
  import org.eclipse.jdt.internal.compiler.ASTVisitor;
  import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
  import org.eclipse.jdt.internal.compiler.codegen.CodeStream;
  import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
  import org.eclipse.jdt.internal.compiler.flow.FlowContext;
  import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
  import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
  import org.eclipse.jdt.internal.compiler.impl.Constant;
  import org.eclipse.jdt.internal.compiler.lookup.Binding;
  import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
  import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
  import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
  import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
  import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
  import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
  import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding;
  import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding;
  import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
  import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
  import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
  import org.eclipse.jdt.internal.compiler.lookup.Scope;
  import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
  import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding;
  import org.eclipse.jdt.internal.compiler.lookup.TagBits;
  import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
  import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
  import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
  import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
  
  public class QualifiedNameReference extends NameReference {
  
  	public char[][] tokens;
  	public long[] sourcePositions;
  	public FieldBinding[] otherBindings;
  	int[] otherDepths;
  	public int indexOfFirstFieldBinding; 	// points into tokens & sourcePositions for the first token that corresponds to first FieldBinding
  										  	// *** the index is 1-based ***
  											// during BlockScope#getBinding(..) it will walk through positions until it finds the first field
  	public SyntheticMethodBinding syntheticWriteAccessor;
  	public SyntheticMethodBinding[] syntheticReadAccessors;
  	public TypeBinding genericCast;
  	public TypeBinding[] otherGenericCasts;
  
  public QualifiedNameReference(char[][] tokens, long[] positions, int sourceStart, int sourceEnd) {
  	this.tokens = tokens;
  	this.sourcePositions = positions;
  	this.sourceStart = sourceStart;
  	this.sourceEnd = sourceEnd;
  }
  
  public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, boolean isCompound) {
  	// determine the rank until which we now we do not need any actual value for the field access
  	int otherBindingsCount = this.otherBindings == null ? 0 : this.otherBindings.length;
  	boolean needValue = otherBindingsCount == 0 || !this.otherBindings[0].isStatic();
  	boolean complyTo14 = currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4;
  	FieldBinding lastFieldBinding = null;
  	switch (this.bits & ASTNode.RestrictiveFlagMASK) {
  		case Binding.FIELD : // reading a field
  			lastFieldBinding = (FieldBinding) this.binding;
  			if (needValue || complyTo14) {
  				manageSyntheticAccessIfNecessary(currentScope, lastFieldBinding, 0, flowInfo);
  			}
  			// check if final blank field
  			if (lastFieldBinding.isBlankFinal()
  				    && this.otherBindings != null // the last field binding is only assigned
  	 				&& currentScope.needBlankFinalFieldInitializationCheck(lastFieldBinding)) {
  				FlowInfo fieldInits = flowContext.getInitsForFinalBlankInitializationCheck(lastFieldBinding.declaringClass.original(), flowInfo);
  				if (!fieldInits.isDefinitelyAssigned(lastFieldBinding)) {
  					currentScope.problemReporter().uninitializedBlankFinalField(lastFieldBinding, this);
  				}
  			}
  			break;
  		case Binding.LOCAL :
  			// first binding is a local variable
  			LocalVariableBinding localBinding;
  			if (!flowInfo
  				.isDefinitelyAssigned(localBinding = (LocalVariableBinding) this.binding)) {
  				currentScope.problemReporter().uninitializedLocalVariable(localBinding, this);
  			}
  			if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0)	{
  				localBinding.useFlag = LocalVariableBinding.USED;
  			} else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
  				localBinding.useFlag = LocalVariableBinding.FAKE_USED;
  			}
  			if (needValue) {
  				checkInternalNPE(currentScope, flowContext, flowInfo, true);
  			}
  	}
  
  	if (needValue) {
  		manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
  		// only for first binding
  	}
  	// all intermediate field accesses are read accesses
  	if (this.otherBindings != null) {
  		for (int i = 0; i < otherBindingsCount-1; i++) {
  			lastFieldBinding = this.otherBindings[i];
  			needValue = !this.otherBindings[i+1].isStatic();
  			if (needValue || complyTo14) {
  				manageSyntheticAccessIfNecessary(currentScope, lastFieldBinding, i + 1, flowInfo);
  			}
  		}
  		lastFieldBinding = this.otherBindings[otherBindingsCount-1];
  	}
  
  	if (isCompound) {
  		if (otherBindingsCount == 0
  				&& lastFieldBinding.isBlankFinal()
  				&& currentScope.needBlankFinalFieldInitializationCheck(lastFieldBinding)) {
  			FlowInfo fieldInits = flowContext.getInitsForFinalBlankInitializationCheck(lastFieldBinding.declaringClass, flowInfo);
  			if (!fieldInits.isDefinitelyAssigned(lastFieldBinding)) {
  				currentScope.problemReporter().uninitializedBlankFinalField(lastFieldBinding, this);
  			}
  		}
  		manageSyntheticAccessIfNecessary(currentScope, lastFieldBinding, otherBindingsCount, flowInfo);
  	}
  
  	if (assignment.expression != null) {
  		flowInfo =
  			assignment
  				.expression
  				.analyseCode(currentScope, flowContext, flowInfo)
  				.unconditionalInits();
  	}
  
  	// the last field access is a write access
  	if (lastFieldBinding.isFinal()) {
  		// in a context where it can be assigned?
  		if (otherBindingsCount == 0
  				&& this.indexOfFirstFieldBinding == 1
  				&& lastFieldBinding.isBlankFinal()
  				&& !isCompound
  				&& currentScope.allowBlankFinalFieldAssignment(lastFieldBinding)) {
  			if (flowInfo.isPotentiallyAssigned(lastFieldBinding)) {
  				currentScope.problemReporter().duplicateInitializationOfBlankFinalField(lastFieldBinding, this);
  			} else {
  				flowContext.recordSettingFinal(lastFieldBinding, this, flowInfo);
  			}
  			flowInfo.markAsDefinitelyAssigned(lastFieldBinding);
  		} else {
  			currentScope.problemReporter().cannotAssignToFinalField(lastFieldBinding, this);
  			if (otherBindingsCount == 0 && currentScope.allowBlankFinalFieldAssignment(lastFieldBinding)) { // pretend it got assigned
  				flowInfo.markAsDefinitelyAssigned(lastFieldBinding);
  			}
  		}
  	}
  	// note: not covering def.assign for @NonNull: QNR cannot provably refer to a variable of the current object
  	manageSyntheticAccessIfNecessary(currentScope, lastFieldBinding, -1 /*write-access*/, flowInfo);
  
  	return flowInfo;
  }
  
  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
  	return analyseCode(currentScope, flowContext, flowInfo, true);
  }
  
  public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) {
  	// determine the rank until which we now we do not need any actual value for the field access
  	int otherBindingsCount = this.otherBindings == null ? 0 : this.otherBindings.length;
  
  	boolean needValue = otherBindingsCount == 0 ? valueRequired : !this.otherBindings[0].isStatic();
  	boolean complyTo14 = currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4;
  	switch (this.bits & ASTNode.RestrictiveFlagMASK) {
  		case Binding.FIELD : // reading a field
  			if (needValue || complyTo14) {
  				manageSyntheticAccessIfNecessary(currentScope, (FieldBinding) this.binding, 0, flowInfo);
  			}
  			FieldBinding fieldBinding = (FieldBinding) this.binding;
  			if (this.indexOfFirstFieldBinding == 1) { // was an implicit reference to the first field binding
  				// check if reading a final blank field
  				if (fieldBinding.isBlankFinal()
  						&& currentScope.needBlankFinalFieldInitializationCheck(fieldBinding)) {
  					FlowInfo fieldInits = flowContext.getInitsForFinalBlankInitializationCheck(fieldBinding.declaringClass.original(), flowInfo);
  					if (!fieldInits.isDefinitelyAssigned(fieldBinding)) {
  						currentScope.problemReporter().uninitializedBlankFinalField(fieldBinding, this);
  					}
  				}
  			}
  			break;
  		case Binding.LOCAL : // reading a local variable
  			LocalVariableBinding localBinding;
  			if (!flowInfo.isDefinitelyAssigned(localBinding = (LocalVariableBinding) this.binding)) {
  				currentScope.problemReporter().uninitializedLocalVariable(localBinding, this);
  			}
  			if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
  				localBinding.useFlag = LocalVariableBinding.USED;
  			} else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
  				localBinding.useFlag = LocalVariableBinding.FAKE_USED;
  			}
  	}
  	if (needValue) {
  		checkInternalNPE(currentScope, flowContext, flowInfo, true);
  	}
  	if (needValue) {
  		manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
  		// only for first binding (if value needed only)
  	}
  	if (this.otherBindings != null) {
  		for (int i = 0; i < otherBindingsCount; i++) {
  			needValue = i < otherBindingsCount-1 ? !this.otherBindings[i+1].isStatic() : valueRequired;
  			if (needValue || complyTo14) {
  				manageSyntheticAccessIfNecessary(currentScope, this.otherBindings[i], i + 1, flowInfo);
  			}
  		}
  	}
  	return flowInfo;
  }
  
  /* check if any dot in this QNR may trigger an NPE. */
  private void checkInternalNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, boolean checkString) {
  	if ((this.bits & ASTNode.RestrictiveFlagMASK) == Binding.LOCAL) {
  		LocalVariableBinding local = (LocalVariableBinding) this.binding;
  		if (local != null &&
  			(local.type.tagBits & TagBits.IsBaseType) == 0 &&
  			(checkString || local.type.id != TypeIds.T_JavaLangString)) {
  			if ((this.bits & ASTNode.IsNonNull) == 0) {
  				flowContext.recordUsingNullReference(scope, local, this,
  					FlowContext.MAY_NULL, flowInfo);
  			}
  			flowInfo.markAsComparedEqualToNonNull(local);
  			// from thereon it is set
  			flowContext.markFinallyNullStatus(local, FlowInfo.NON_NULL);
  		}
  	}
  	if (this.otherBindings != null) {
  		if ((this.bits & ASTNode.RestrictiveFlagMASK) == Binding.FIELD) {
  			// is the first field dereferenced annotated Nullable? If so, report immediately
  			checkNullableFieldDereference(scope, (FieldBinding) this.binding, this.sourcePositions[this.indexOfFirstFieldBinding-1]);
  		}
  		// look for annotated fields, they do not depend on flow context -> check immediately:
  		int length = this.otherBindings.length - 1; // don't check the last binding
  		for (int i = 0; i < length; i++) {
  			checkNullableFieldDereference(scope, this.otherBindings[i], this.sourcePositions[this.indexOfFirstFieldBinding+i]);
  		}
  	}
  }
  
  public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
  	if (super.checkNPE(scope, flowContext, flowInfo)) {
  		return true;
  	}
  	FieldBinding fieldBinding = null;
  	long position = 0L;
  	if (this.otherBindings == null) {
  		if ((this.bits & RestrictiveFlagMASK) == Binding.FIELD) {
  			fieldBinding = (FieldBinding) this.binding;
  			position = this.sourcePositions[0];
  		}
  	} else {
  		fieldBinding = this.otherBindings[this.otherBindings.length - 1];
  		position = this.sourcePositions[this.sourcePositions.length - 1];
  	}
  	if (fieldBinding != null) {
  		return checkNullableFieldDereference(scope, fieldBinding, position);
  	}
  	return false;
  }
  
  /**
   * @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)
   */
  public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) {
  	if (runtimeTimeType == null || compileTimeType == null)
  		return;
  	// set the generic cast after the fact, once the type expectation is fully known (no need for strict cast)
  	FieldBinding field = null;
  	int length = this.otherBindings == null ? 0 : this.otherBindings.length;
  	if (length == 0) {
  		if ((this.bits & Binding.FIELD) != 0 && this.binding != null && this.binding.isValidBinding()) {
  			field = (FieldBinding) this.binding;
  		}
  	} else {
  		field  = this.otherBindings[length-1];
  	}
  	if (field != null) {
  		FieldBinding originalBinding = field.original();
  		TypeBinding originalType = originalBinding.type;
  		// extra cast needed if field type is type variable
  		if (originalType.leafComponentType().isTypeVariable()) {
  			TypeBinding targetType = (!compileTimeType.isBaseType() && runtimeTimeType.isBaseType())
  			? compileTimeType  // unboxing: checkcast before conversion
  					: runtimeTimeType;
  			TypeBinding typeCast = originalType.genericCast(targetType);
  			setGenericCast(length, typeCast);
  			if (typeCast instanceof ReferenceBinding) {
  				ReferenceBinding referenceCast = (ReferenceBinding) typeCast;
  				if (!referenceCast.canBeSeenBy(scope)) {
  					scope.problemReporter().invalidType(this,
  							new ProblemReferenceBinding(
  									CharOperation.splitOn('.', referenceCast.shortReadableName()),
  									referenceCast,
  									ProblemReasons.NotVisible));
  				}
  			}
  		}
  	}
  	super.computeConversion(scope, runtimeTimeType, compileTimeType);
  }
  
  public void generateAssignment(BlockScope currentScope, CodeStream codeStream, Assignment assignment, boolean valueRequired) {
  	int pc = codeStream.position;
  	FieldBinding lastFieldBinding = generateReadSequence(currentScope, codeStream);
  	codeStream.recordPositionsFrom(pc , this.sourceStart);
  	assignment.expression.generateCode(currentScope, codeStream, true);
  	fieldStore(currentScope, codeStream, lastFieldBinding, this.syntheticWriteAccessor, getFinalReceiverType(), false /*implicit this*/, valueRequired);
  	// equivalent to valuesRequired[maxOtherBindings]
  	if (valueRequired) {
  		codeStream.generateImplicitConversion(assignment.implicitConversion);
  	}
  }
  
  public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
  	int pc = codeStream.position;
  	if (this.constant != Constant.NotAConstant) {
  		if (valueRequired) {
  			codeStream.generateConstant(this.constant, this.implicitConversion);
  		}
  	} else {
  		FieldBinding lastFieldBinding = generateReadSequence(currentScope, codeStream);
  		if (lastFieldBinding != null) {
  			boolean isStatic = lastFieldBinding.isStatic();
  			Constant fieldConstant = lastFieldBinding.constant();
  			if (fieldConstant != Constant.NotAConstant) {
  				if (!isStatic){
  					codeStream.invokeObjectGetClass();
  					codeStream.pop();
  				}
  				if (valueRequired) { // inline the last field constant
  					codeStream.generateConstant(fieldConstant, this.implicitConversion);
  				}
  			} else {
  				boolean isFirst = lastFieldBinding == this.binding
  												&& (this.indexOfFirstFieldBinding == 1 || TypeBinding.equalsEquals(lastFieldBinding.declaringClass, currentScope.enclosingReceiverType()))
  												&& this.otherBindings == null; // could be dup: next.next.next
  				TypeBinding requiredGenericCast = getGenericCast(this.otherBindings == null ? 0 : this.otherBindings.length);
  				if (valueRequired
  						|| (!isFirst && currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4)
  						|| ((this.implicitConversion & TypeIds.UNBOXING) != 0)
  						|| requiredGenericCast != null) {
  					int lastFieldPc = codeStream.position;
  					if (lastFieldBinding.declaringClass == null) { // array length
  						codeStream.arraylength();
  						if (valueRequired) {
  							codeStream.generateImplicitConversion(this.implicitConversion);
  						} else {
  							// could occur if !valueRequired but compliance >= 1.4
  							codeStream.pop();
  						}
  					} else {
  						SyntheticMethodBinding accessor = this.syntheticReadAccessors == null ? null : this.syntheticReadAccessors[this.syntheticReadAccessors.length - 1];
  						if (accessor == null) {
  							TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, lastFieldBinding, getFinalReceiverType(), isFirst);
  							if (isStatic) {
  								codeStream.fieldAccess(Opcodes.OPC_getstatic, lastFieldBinding, constantPoolDeclaringClass);
  							} else {
  								codeStream.fieldAccess(Opcodes.OPC_getfield, lastFieldBinding, constantPoolDeclaringClass);
  							}
  						} else {
  							codeStream.invoke(Opcodes.OPC_invokestatic, accessor, null /* default declaringClass */);
  						}
  						if (requiredGenericCast != null) codeStream.checkcast(requiredGenericCast);
  						if (valueRequired) {
  							codeStream.generateImplicitConversion(this.implicitConversion);
  						} else {
  							boolean isUnboxing = (this.implicitConversion & TypeIds.UNBOXING) != 0;
  							// conversion only generated if unboxing
  							if (isUnboxing) codeStream.generateImplicitConversion(this.implicitConversion);
  							switch (isUnboxing ? postConversionType(currentScope).id : lastFieldBinding.type.id) {
  								case T_long :
  								case T_double :
  									codeStream.pop2();
  									break;
  								default :
  									codeStream.pop();
  									break;
  							}
  						}
  					}
  
  					int fieldPosition = (int) (this.sourcePositions[this.sourcePositions.length - 1] >>> 32);
  					codeStream.recordPositionsFrom(lastFieldPc, fieldPosition);
  				} else {
  					if (!isStatic){
  						codeStream.invokeObjectGetClass(); // perform null check
  						codeStream.pop();
  					}
  				}
  			}
  		}
  	}
  	codeStream.recordPositionsFrom(pc, this.sourceStart);
  }
  
  public void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired) {
  	FieldBinding lastFieldBinding = generateReadSequence(currentScope, codeStream);
  	// check if compound assignment is the only usage of a private field
  	reportOnlyUselesslyReadPrivateField(currentScope, lastFieldBinding, valueRequired);
  	boolean isFirst = lastFieldBinding == this.binding
  		&& (this.indexOfFirstFieldBinding == 1 || TypeBinding.equalsEquals(lastFieldBinding.declaringClass, currentScope.enclosingReceiverType()))
  		&& this.otherBindings == null; // could be dup: next.next.next
  	TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, lastFieldBinding, getFinalReceiverType(), isFirst);			
  	SyntheticMethodBinding accessor = this.syntheticReadAccessors == null ? null : this.syntheticReadAccessors[this.syntheticReadAccessors.length - 1];
  	if (lastFieldBinding.isStatic()) {
  		if (accessor == null) {
  			codeStream.fieldAccess(Opcodes.OPC_getstatic, lastFieldBinding, constantPoolDeclaringClass);
  		} else {
  			codeStream.invoke(Opcodes.OPC_invokestatic, accessor, null /* default declaringClass */);
  		}
  	} else {
  		codeStream.dup();
  		if (accessor == null) {
  			codeStream.fieldAccess(Opcodes.OPC_getfield, lastFieldBinding, constantPoolDeclaringClass);
  		} else {
  			codeStream.invoke(Opcodes.OPC_invokestatic, accessor, null /* default declaringClass */);
  		}
  	}
  	// the last field access is a write access
  	// perform the actual compound operation
  	int operationTypeID;
  	switch(operationTypeID = (this.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4) {
  		case T_JavaLangString :
  		case T_JavaLangObject :
  		case T_undefined :
  			codeStream.generateStringConcatenationAppend(currentScope, null, expression);
  			break;
  		default :
  			TypeBinding requiredGenericCast = getGenericCast(this.otherBindings == null ? 0 : this.otherBindings.length);
  			if (requiredGenericCast != null) codeStream.checkcast(requiredGenericCast);
  			// promote the array reference to the suitable operation type
  			codeStream.generateImplicitConversion(this.implicitConversion);
  			// generate the increment value (will by itself  be promoted to the operation value)
  			if (expression == IntLiteral.One) { // prefix operation
  				codeStream.generateConstant(expression.constant, this.implicitConversion);
  			} else {
  				expression.generateCode(currentScope, codeStream, true);
  			}
  			// perform the operation
  			codeStream.sendOperator(operator, operationTypeID);
  			// cast the value back to the array reference type
  			codeStream.generateImplicitConversion(assignmentImplicitConversion);
  	}
  	// actual assignment
  	fieldStore(currentScope, codeStream, lastFieldBinding, this.syntheticWriteAccessor, getFinalReceiverType(), false /*implicit this*/, valueRequired);
  	// equivalent to valuesRequired[maxOtherBindings]
  }
  
  public void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired) {
  	FieldBinding lastFieldBinding = generateReadSequence(currentScope, codeStream);
  	// check if this post increment is the only usage of a private field
  	reportOnlyUselesslyReadPrivateField(currentScope, lastFieldBinding, valueRequired);
  	boolean isFirst = lastFieldBinding == this.binding
  		&& (this.indexOfFirstFieldBinding == 1 || TypeBinding.equalsEquals(lastFieldBinding.declaringClass, currentScope.enclosingReceiverType()))
  		&& this.otherBindings == null; // could be dup: next.next.next
  	TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, lastFieldBinding, getFinalReceiverType(), isFirst);			
  	SyntheticMethodBinding accessor = this.syntheticReadAccessors == null
  			? null
  			: this.syntheticReadAccessors[this.syntheticReadAccessors.length - 1];
  	if (lastFieldBinding.isStatic()) {
  		if (accessor == null) {
  			codeStream.fieldAccess(Opcodes.OPC_getstatic, lastFieldBinding, constantPoolDeclaringClass);
  		} else {
  			codeStream.invoke(Opcodes.OPC_invokestatic, accessor, constantPoolDeclaringClass);
  		}
  	} else {
  		codeStream.dup();
  		if (accessor == null) {
  			codeStream.fieldAccess(Opcodes.OPC_getfield, lastFieldBinding, null /* default declaringClass */);
  		} else {
  			codeStream.invoke(Opcodes.OPC_invokestatic, accessor, null /* default declaringClass */);
  		}
  	}
  	TypeBinding requiredGenericCast = getGenericCast(this.otherBindings == null ? 0 : this.otherBindings.length);
  	TypeBinding operandType;
  	if (requiredGenericCast != null) {
  		codeStream.checkcast(requiredGenericCast);
  		operandType = requiredGenericCast;
  	} else {
  		operandType = lastFieldBinding.type;
  	}		
  	// duplicate the old field value
  	if (valueRequired) {
  		if (lastFieldBinding.isStatic()) {
  			switch (operandType.id) {
  				case TypeIds.T_long :
  				case TypeIds.T_double :
  					codeStream.dup2();
  					break;
  				default:
  					codeStream.dup();
  					break;
  			}			
  		} else { // Stack:  [owner][old field value]  ---> [old field value][owner][old field value]
  			switch (operandType.id) {
  				case TypeIds.T_long :
  				case TypeIds.T_double :
  					codeStream.dup2_x1();
  					break;
  				default:
  					codeStream.dup_x1();
  					break;
  			}			
  		}
  	}
  	codeStream.generateImplicitConversion(this.implicitConversion);		
  	codeStream.generateConstant(
  		postIncrement.expression.constant,
  		this.implicitConversion);
  	codeStream.sendOperator(postIncrement.operator, this.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
  	codeStream.generateImplicitConversion(
  		postIncrement.preAssignImplicitConversion);
  	fieldStore(currentScope, codeStream, lastFieldBinding, this.syntheticWriteAccessor, getFinalReceiverType(), false /*implicit this*/, false);
  }
  
  /*
   * Generate code for all bindings (local and fields) excluding the last one, which may then be generated code
   * for a read or write access.
   */
  public FieldBinding generateReadSequence(BlockScope currentScope, CodeStream codeStream) {
  	// determine the rank until which we now we do not need any actual value for the field access
  	int otherBindingsCount = this.otherBindings == null ? 0 : this.otherBindings.length;
  	boolean needValue = otherBindingsCount == 0 || !this.otherBindings[0].isStatic();
  	FieldBinding lastFieldBinding;
  	TypeBinding lastGenericCast;
  	TypeBinding lastReceiverType;
  	boolean complyTo14 = currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4;
  
  	switch (this.bits & ASTNode.RestrictiveFlagMASK) {
  		case Binding.FIELD :
  			lastFieldBinding = ((FieldBinding) this.binding).original();
  			lastGenericCast = this.genericCast;
  			lastReceiverType = this.actualReceiverType;
  			// if first field is actually constant, we can inline it
  			if (lastFieldBinding.constant() != Constant.NotAConstant) {
  				break;
  			}
  			if ((needValue && !lastFieldBinding.isStatic()) || lastGenericCast != null) {
  				int pc = codeStream.position;
  				if ((this.bits & ASTNode.DepthMASK) != 0) {
  					ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT);
  					Object[] emulationPath = currentScope.getEmulationPath(targetType, true /*only exact match*/, false/*consider enclosing arg*/);
  					codeStream.generateOuterAccess(emulationPath, this, targetType, currentScope);
  				} else {
  					generateReceiver(codeStream);
  				}
  				codeStream.recordPositionsFrom(pc, this.sourceStart);
  			}
  			break;
  		case Binding.LOCAL : // reading the first local variable
  			lastFieldBinding = null;
  			lastGenericCast = null;
  			LocalVariableBinding localBinding = (LocalVariableBinding) this.binding;
  			lastReceiverType = localBinding.type;
  			if (!needValue) break; // no value needed
  			// regular local variable read
  			Constant localConstant = localBinding.constant();
  			if (localConstant != Constant.NotAConstant) {
  				codeStream.generateConstant(localConstant, 0);
  				// no implicit conversion
  			} else {
  				// outer local?
  				if ((this.bits & ASTNode.IsCapturedOuterLocal) != 0) {
  					checkEffectiveFinality(localBinding, currentScope);
  					// outer local can be reached either through a synthetic arg or a synthetic field
  					VariableBinding[] path = currentScope.getEmulationPath(localBinding);
  					codeStream.generateOuterAccess(path, this, localBinding, currentScope);
  				} else {
  					codeStream.load(localBinding);
  				}
  			}
  			break;
  		default : // should not occur
  			return null;
  	}
  
  	// all intermediate field accesses are read accesses
  	// only the last field binding is a write access
  	int positionsLength = this.sourcePositions.length;
  	FieldBinding initialFieldBinding = lastFieldBinding; // can be null if initial was a local binding
  	if (this.otherBindings != null) {
  		for (int i = 0; i < otherBindingsCount; i++) {
  			int pc = codeStream.position;
  			FieldBinding nextField = this.otherBindings[i].original();
  			TypeBinding nextGenericCast = this.otherGenericCasts == null ? null : this.otherGenericCasts[i];
  			if (lastFieldBinding != null) {
  				needValue = !nextField.isStatic();
  				Constant fieldConstant = lastFieldBinding.constant();
  				if (fieldConstant != Constant.NotAConstant) {
  					if (i > 0 && !lastFieldBinding.isStatic()) {
  						codeStream.invokeObjectGetClass(); // perform null check
  						codeStream.pop();
  					}
  					if (needValue) {
  						codeStream.generateConstant(fieldConstant, 0);
  					}
  				} else {
  					if (needValue || (i > 0 && complyTo14) || lastGenericCast != null) {
  						MethodBinding accessor = this.syntheticReadAccessors == null ? null : this.syntheticReadAccessors[i];
  						if (accessor == null) {
  							TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, lastFieldBinding, lastReceiverType, i == 0 && this.indexOfFirstFieldBinding == 1);
  							if (lastFieldBinding.isStatic()) {
  								codeStream.fieldAccess(Opcodes.OPC_getstatic, lastFieldBinding, constantPoolDeclaringClass);
  							} else {
  								codeStream.fieldAccess(Opcodes.OPC_getfield, lastFieldBinding, constantPoolDeclaringClass);
  							}
  						} else {
  							codeStream.invoke(Opcodes.OPC_invokestatic, accessor, null /* default declaringClass */);
  						}
  						if (lastGenericCast != null) {
  							codeStream.checkcast(lastGenericCast);
  							lastReceiverType = lastGenericCast;
  						} else {
  							lastReceiverType = lastFieldBinding.type;
  						}
  