package org.eclipse.jdt.internal.eval;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
import org.eclipse.jdt.internal.compiler.ast.CompoundAssignment;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
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.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.ParameterizedFieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
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.AbortMethod;
public class CodeSnippetSingleNameReference extends SingleNameReference implements EvaluationConstants, ProblemReasons {
EvaluationContext evaluationContext;
FieldBinding delegateThis;
public CodeSnippetSingleNameReference(char[] source, long pos, EvaluationContext evaluationContext) {
super(source, pos);
this.evaluationContext = evaluationContext;
}
@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) {
switch (this.bits & RestrictiveFlagMASK) {
case Binding.FIELD :
FieldBinding fieldBinding;
if ((fieldBinding = (FieldBinding) this.binding).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 :
LocalVariableBinding localBinding;
if (!flowInfo.isDefinitelyAssigned(localBinding = (LocalVariableBinding) this.binding)) {
currentScope.problemReporter().uninitializedLocalVariable(localBinding, this, currentScope);
}
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
localBinding.useFlag = LocalVariableBinding.USED;
} else if (localBinding.useFlag == LocalVariableBinding.UNUSED) {
localBinding.useFlag = LocalVariableBinding.FAKE_USED;
}
}
return flowInfo;
}
@Override
public TypeBinding checkFieldAccess(BlockScope scope) {
if (this.delegateThis == null) {
return super.checkFieldAccess(scope);
}
FieldBinding fieldBinding = (FieldBinding) this.binding;
this.bits &= ~RestrictiveFlagMASK;
this.bits |= Binding.FIELD;
if (!fieldBinding.isStatic()) {
if (this.evaluationContext.isStatic) {
scope.problemReporter().staticFieldAccessToNonStaticVariable(
this,
fieldBinding);
this.constant = Constant.NotAConstant;
return null;
}
}
this.constant = fieldBinding.constant(scope);
if (isFieldUseDeprecated(fieldBinding, scope, this.bits)) {
scope.problemReporter().deprecatedField(fieldBinding, this);
}
return fieldBinding.type;
}
@Override
public void generateAssignment(BlockScope currentScope, CodeStream codeStream, Assignment assignment, boolean valueRequired) {
if (assignment.expression.isCompactableOperation()) {
BinaryExpression operation = (BinaryExpression) assignment.expression;
int operator = (operation.bits & OperatorMASK) >> OperatorSHIFT;
SingleNameReference variableReference;
if ((operation.left instanceof SingleNameReference) && ((variableReference = (SingleNameReference) operation.left).binding == this.binding)) {
variableReference.generateCompoundAssignment(currentScope, codeStream, this.syntheticAccessors == null ? null : this.syntheticAccessors[WRITE], operation.right, operator, operation.implicitConversion, valueRequired);
if (valueRequired) {
codeStream.generateImplicitConversion(assignment.implicitConversion);
}
return;
}
if ((operation.right instanceof SingleNameReference)
&& ((operator == PLUS) || (operator == MULTIPLY))
&& ((variableReference = (SingleNameReference) operation.right).binding == this.binding)
&& (operation.left.constant != Constant.NotAConstant)
&& (((operation.left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) != T_JavaLangString)
&& (((operation.right.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) != T_JavaLangString)) {
variableReference.generateCompoundAssignment(currentScope, codeStream, this.syntheticAccessors == null ? null : this.syntheticAccessors[WRITE], operation.left, operator, operation.implicitConversion, valueRequired);
if (valueRequired) {
codeStream.generateImplicitConversion(assignment.implicitConversion);
}
return;
}
}
switch (this.bits & RestrictiveFlagMASK) {
case Binding.FIELD :
FieldBinding codegenField = ((FieldBinding) this.binding).original();
if (codegenField.canBeSeenBy(getReceiverType(currentScope), this, currentScope)) {
if (!codegenField.isStatic()) {
if ((this.bits & DepthMASK) != 0) {
ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((this.bits & DepthMASK) >> DepthSHIFT);
Object[] emulationPath = currentScope.getEmulationPath(targetType, true , false);
codeStream.generateOuterAccess(emulationPath, this, targetType, currentScope);
} else {
generateReceiver(codeStream);
}
}
assignment.expression.generateCode(currentScope, codeStream, true);
fieldStore(currentScope, codeStream, codegenField, null, this.actualReceiverType, this.delegateThis == null , valueRequired);
if (valueRequired) {
codeStream.generateImplicitConversion(assignment.implicitConversion);
}
} else {
codeStream.generateEmulationForField(codegenField);
if (!codegenField.isStatic()) {
if ((this.bits & DepthMASK) != 0) {
currentScope.problemReporter().needImplementation(this);
} else {
generateReceiver(codeStream);
}
} else {
codeStream.aconst_null();
}
assignment.expression.generateCode(currentScope, codeStream, true);
if (valueRequired) {
if ((TypeBinding.equalsEquals(codegenField.type, TypeBinding.LONG)) || (TypeBinding.equalsEquals(codegenField.type, TypeBinding.DOUBLE))) {
codeStream.dup2_x2();
} else {
codeStream.dup_x2();
}
}
codeStream.generateEmulatedWriteAccessForField(codegenField);
if (valueRequired) {
codeStream.generateImplicitConversion(assignment.implicitConversion);
}
}
return;
case Binding.LOCAL :
LocalVariableBinding localBinding = (LocalVariableBinding) this.binding;
if (localBinding.resolvedPosition != -1) {
assignment.expression.generateCode(currentScope, codeStream, true);
} else {
if (assignment.expression.constant != Constant.NotAConstant) {
if (valueRequired) {
codeStream.generateConstant(assignment.expression.constant, assignment.implicitConversion);
}
} else {
assignment.expression.generateCode(currentScope, codeStream, true);
if (valueRequired) {
codeStream.generateImplicitConversion(assignment.implicitConversion);
} else {
if ((TypeBinding.equalsEquals(localBinding.type, TypeBinding.LONG)) || (TypeBinding.equalsEquals(localBinding.type, TypeBinding.DOUBLE))) {
codeStream.pop2();
} else {
codeStream.pop();
}
}
}
return;
}
codeStream.store(localBinding, valueRequired);
if ((this.bits & FirstAssignmentToLocal) != 0) {
localBinding.recordInitializationStartPC(codeStream.position);
}
if (valueRequired) {
codeStream.generateImplicitConversion(assignment.implicitConversion);
}
}
}
@Override
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 {
switch (this.bits & RestrictiveFlagMASK) {
case Binding.FIELD :
if (!valueRequired)
break;
FieldBinding codegenField = ((FieldBinding) this.binding).original();
Constant fieldConstant = codegenField.constant();
if (fieldConstant == Constant.NotAConstant) {
if (codegenField.canBeSeenBy(getReceiverType(currentScope), this, currentScope)) {
TypeBinding someReceiverType = this.delegateThis != null ? this.delegateThis.type : this.actualReceiverType;
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenField, someReceiverType, true );
if (codegenField.isStatic()) {
codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenField, constantPoolDeclaringClass);
} else {
if ((this.bits & DepthMASK) != 0) {
ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((this.bits & DepthMASK) >> DepthSHIFT);
Object[] emulationPath = currentScope.getEmulationPath(targetType, true , false);
codeStream.generateOuterAccess(emulationPath, this, targetType, currentScope);
} else {
generateReceiver(codeStream);
}
codeStream.fieldAccess(Opcodes.OPC_getfield, codegenField, constantPoolDeclaringClass);
}
} else {
if (!codegenField.isStatic()) {
if ((this.bits & DepthMASK) != 0) {
currentScope.problemReporter().needImplementation(this);
} else {
generateReceiver(codeStream);
}
} else {
codeStream.aconst_null();
}
codeStream.generateEmulatedReadAccessForField(codegenField);
}
if (this.genericCast != null) codeStream.checkcast(this.genericCast);
codeStream.generateImplicitConversion(this.implicitConversion);
} else {
codeStream.generateConstant(fieldConstant, this.implicitConversion);
}
break;
case Binding.LOCAL :
LocalVariableBinding localBinding = (LocalVariableBinding) this.binding;
if (localBinding.resolvedPosition == -1) {
if (valueRequired) {
localBinding.useFlag = LocalVariableBinding.USED;
throw new AbortMethod(CodeStream.RESTART_CODE_GEN_FOR_UNUSED_LOCALS_MODE, null);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
return;
}
if (!valueRequired)
break;
if ((this.bits & IsCapturedOuterLocal) != 0) {
checkEffectiveFinality(localBinding, currentScope);
VariableBinding[] path = currentScope.getEmulationPath(localBinding);
codeStream.generateOuterAccess(path, this, localBinding, currentScope);
} else {
codeStream.load(localBinding);
}
codeStream.generateImplicitConversion(this.implicitConversion);
break;
}
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
@Override
public void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, MethodBinding writeAccessor, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired) {
switch (this.bits & RestrictiveFlagMASK) {
case Binding.FIELD :
FieldBinding codegenField = ((FieldBinding) this.binding).original();
if (codegenField.isStatic()) {
if (codegenField.canBeSeenBy(getReceiverType(currentScope), this, currentScope)) {
TypeBinding someReceiverType = this.delegateThis != null ? this.delegateThis.type : this.actualReceiverType;
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenField, someReceiverType, true );
codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenField, constantPoolDeclaringClass);
} else {
codeStream.generateEmulationForField(codegenField);
codeStream.aconst_null();
codeStream.aconst_null();
codeStream.generateEmulatedReadAccessForField(codegenField);
}
} else {
if (codegenField.canBeSeenBy(getReceiverType(currentScope), this, currentScope)) {
if ((this.bits & DepthMASK) != 0) {
ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((this.bits & DepthMASK) >> DepthSHIFT);
Object[] emulationPath = currentScope.getEmulationPath(targetType, true , false);
codeStream.generateOuterAccess(emulationPath, this, targetType, currentScope);
} else {
generateReceiver(codeStream);
}
codeStream.dup();
TypeBinding someReceiverType = this.delegateThis != null ? this.delegateThis.type : this.actualReceiverType;
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenField, someReceiverType, true );
codeStream.fieldAccess(Opcodes.OPC_getfield, codegenField, constantPoolDeclaringClass);
} else {
if ((this.bits & DepthMASK) != 0) {
currentScope.problemReporter().needImplementation(this);
}
codeStream.generateEmulationForField(codegenField);
generateReceiver(codeStream);
codeStream.dup();
codeStream.generateEmulatedReadAccessForField(codegenField);
}
}
break;
case Binding.LOCAL :
LocalVariableBinding localBinding = (LocalVariableBinding) this.binding;
Constant assignConstant;
switch (localBinding.type.id) {
case T_JavaLangString :
codeStream.generateStringConcatenationAppend(currentScope, this, expression);
if (valueRequired) {
codeStream.dup();
}
codeStream.store(localBinding, false);
return;
case T_int :
assignConstant = expression.constant;
if (localBinding.resolvedPosition == -1) {
if (valueRequired) {
localBinding.useFlag = LocalVariableBinding.USED;
throw new AbortMethod(CodeStream.RESTART_CODE_GEN_FOR_UNUSED_LOCALS_MODE, null);
} else if (assignConstant == Constant.NotAConstant) {
expression.generateCode(currentScope, codeStream, false);
}
return;
}
if ((assignConstant != Constant.NotAConstant)
&& (assignConstant.typeID() != TypeIds.T_float)
&& (assignConstant.typeID() != TypeIds.T_double)) {
switch (operator) {
case PLUS :
int increment = assignConstant.intValue();
if (increment != (short) increment) break;
codeStream.iinc(localBinding.resolvedPosition, increment);
if (valueRequired) {
codeStream.load(localBinding);
}
return;
case MINUS :
increment = -assignConstant.intValue();
if (increment != (short) increment) break;
codeStream.iinc(localBinding.resolvedPosition, increment);
if (valueRequired) {
codeStream.load(localBinding);
}
return;
}
}
default :
if (localBinding.resolvedPosition == -1) {
assignConstant = expression.constant;
if (valueRequired) {
localBinding.useFlag = LocalVariableBinding.USED;
throw new AbortMethod(CodeStream.RESTART_CODE_GEN_FOR_UNUSED_LOCALS_MODE, null);
} else if (assignConstant == Constant.NotAConstant) {
expression.generateCode(currentScope, codeStream, false);
}
return;
}
codeStream.load(localBinding);
}
}
int operationTypeID;
switch(operationTypeID = (this.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) {
case T_JavaLangString :
case T_JavaLangObject :
case T_undefined :
codeStream.generateStringConcatenationAppend(currentScope, null, expression);
break;
default :
codeStream.generateImplicitConversion(this.implicitConversion);
if (expression == IntLiteral.One){
codeStream.generateConstant(expression.constant, this.implicitConversion);
} else {
expression.generateCode(currentScope, codeStream, true);
}
codeStream.sendOperator(operator, operationTypeID);
codeStream.generateImplicitConversion(assignmentImplicitConversion);
}
switch (this.bits & RestrictiveFlagMASK) {
case Binding.FIELD :
FieldBinding codegenField = ((FieldBinding) this.binding).original();
if (codegenField.canBeSeenBy(getReceiverType(currentScope), this, currentScope)) {
fieldStore(currentScope, codeStream, codegenField, writeAccessor, this.actualReceiverType, this.delegateThis == null , valueRequired);
} else {
if (valueRequired) {
switch (codegenField.type.id) {
case TypeIds.T_long :
case TypeIds.T_double :
codeStream.dup2_x2();
break;
default:
codeStream.dup_x2();
break;
}
}
codeStream.generateEmulatedWriteAccessForField(codegenField);
}
return;
case Binding.LOCAL :
LocalVariableBinding localBinding = (LocalVariableBinding) this.binding;
if (valueRequired) {
switch (localBinding.type.id) {
case TypeIds.T_long :
case TypeIds.T_double :
codeStream.dup2();
break;
default:
codeStream.dup();
break;
}
}
codeStream.store(localBinding, false);
}
}
@Override
public void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired) {
switch (this.bits & RestrictiveFlagMASK) {
case Binding.FIELD :
FieldBinding codegenField = ((FieldBinding) this.binding).original();
if (codegenField.canBeSeenBy(getReceiverType(currentScope), this, currentScope)) {
super.generatePostIncrement(currentScope, codeStream, postIncrement, valueRequired);
} else {
if (codegenField.isStatic()) {
codeStream.aconst_null();
} else {
if ((this.bits & DepthMASK) != 0) {
currentScope.problemReporter().needImplementation(this);
} else {
generateReceiver(codeStream);
}
}
codeStream.generateEmulatedReadAccessForField(codegenField);
if (valueRequired) {
switch (codegenField.type.id) {
case TypeIds.T_long :
case TypeIds.T_double :
codeStream.dup2();
break;
default:
codeStream.dup();
break;
}
}
codeStream.generateEmulationForField(codegenField);
switch (codegenField.type.id) {
case TypeIds.T_long :
case TypeIds.T_double :
codeStream.dup_x2();
codeStream.pop();
if (codegenField.isStatic()) {
codeStream.aconst_null();
} else {
generateReceiver(codeStream);
}
codeStream.dup_x2();
codeStream.pop();
break;
default:
codeStream.dup_x1();
codeStream.pop();
if (codegenField.isStatic()) {
codeStream.aconst_null();
} else {
generateReceiver(codeStream);
}
codeStream.dup_x1();
codeStream.pop();
break;
}
codeStream.generateConstant(postIncrement.expression.constant, this.implicitConversion);
codeStream.sendOperator(postIncrement.operator, codegenField.type.id);
codeStream.generateImplicitConversion(postIncrement.preAssignImplicitConversion);
codeStream.generateEmulatedWriteAccessForField(codegenField);
}
return;
case Binding.LOCAL :
super.generatePostIncrement(currentScope, codeStream, postIncrement, valueRequired);
}
}
@Override
public void generateReceiver(CodeStream codeStream) {
codeStream.aload_0();
if (this.delegateThis != null) {
codeStream.fieldAccess(Opcodes.OPC_getfield, this.delegateThis, null );
}
}
public TypeBinding getReceiverType(BlockScope currentScope) {
Scope scope = currentScope.parent;
while (true) {
switch (scope.kind) {
case Scope.CLASS_SCOPE :
return ((ClassScope) scope).referenceContext.binding;
default:
scope = scope.parent;
}
}
}
@Override
public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess) {
if (this.delegateThis == null) {
super.manageSyntheticAccessIfNecessary(currentScope, flowInfo, isReadAccess);
return;
}
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) return;
if (this.constant != Constant.NotAConstant)
return;
if (this.binding instanceof ParameterizedFieldBinding) {
ParameterizedFieldBinding parameterizedField = (ParameterizedFieldBinding) this.binding;
FieldBinding codegenField = parameterizedField.originalField;
if ((codegenField.type.tagBits & TagBits.HasTypeVariable) != 0) {
this.genericCast = codegenField.type.genericCast(currentScope.boxing(parameterizedField.type));
}
}
}
@Override
public TypeBinding reportError(BlockScope scope) {
this.constant = Constant.NotAConstant;
if (this.binding instanceof ProblemFieldBinding && ((ProblemFieldBinding) this.binding).problemId() == NotFound){
if (this.evaluationContext.declaringTypeName != null) {
this.delegateThis = scope.getField(scope.enclosingSourceType(), DELEGATE_THIS, this);
if (this.delegateThis != null){
this.actualReceiverType = this.delegateThis.type;
this.binding = scope.getField(this.delegateThis.type, this.token, this);
if (!this.binding.isValidBinding()) {
return super.reportError(scope);
}
return checkFieldAccess(scope);
}
}
}
if (this.binding instanceof ProblemBinding && ((ProblemBinding) this.binding).problemId() == NotFound){
if (this.evaluationContext.declaringTypeName != null) {
this.delegateThis = scope.getField(scope.enclosingSourceType(), DELEGATE_THIS, this);
if (this.delegateThis != null){
this.actualReceiverType = this.delegateThis.type;
FieldBinding fieldBinding = scope.getField(this.delegateThis.type, this.token, this);
if (!fieldBinding.isValidBinding()) {
if (((ProblemFieldBinding) fieldBinding).problemId() == NotVisible) {
CodeSnippetScope localScope = new CodeSnippetScope(scope);
this.binding = localScope.getFieldForCodeSnippet(this.delegateThis.type, this.token, this);
return checkFieldAccess(scope);
} else {
return super.reportError(scope);
}
}
this.binding = fieldBinding;
return checkFieldAccess(scope);
}
}
}
return super.reportError(scope);
}
}