/******************************************************************************* | |
* Copyright (c) 2000, 2004 IBM Corporation and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Common Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/cpl-v10.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
* Genady Beriozkin - added support for reporting assignment with no effect | |
*******************************************************************************/ | |
package org.eclipse.wst.jsdt.internal.compiler.ast; | |
import org.eclipse.wst.jsdt.internal.compiler.ASTVisitor; | |
import org.eclipse.wst.jsdt.internal.compiler.codegen.*; | |
import org.eclipse.wst.jsdt.internal.compiler.flow.*; | |
import org.eclipse.wst.jsdt.internal.compiler.lookup.*; | |
public class Assignment extends Expression { | |
public Expression lhs; | |
public Expression expression; | |
public Assignment(Expression lhs, Expression expression, int sourceEnd) { | |
//lhs is always a reference by construction , | |
//but is build as an expression ==> the checkcast cannot fail | |
this.lhs = lhs; | |
lhs.bits |= IsStrictlyAssignedMASK; // tag lhs as assigned | |
this.expression = expression; | |
this.sourceStart = lhs.sourceStart; | |
this.sourceEnd = sourceEnd; | |
} | |
public FlowInfo analyseCode( | |
BlockScope currentScope, | |
FlowContext flowContext, | |
FlowInfo flowInfo) { | |
// record setting a variable: various scenarii are possible, setting an array reference, | |
// a field reference, a blank final field reference, a field of an enclosing instance or | |
// just a local variable. | |
return ((Reference) lhs) | |
.analyseAssignment(currentScope, flowContext, flowInfo, this, false) | |
.unconditionalInits(); | |
} | |
void checkAssignmentEffect(BlockScope scope) { | |
Binding left = getDirectBinding(this.lhs); | |
if (left != null && left == getDirectBinding(this.expression)) { | |
scope.problemReporter().assignmentHasNoEffect(this, left.shortReadableName()); | |
this.bits |= IsAssignmentWithNoEffectMASK; // record assignment has no effect | |
} | |
} | |
void checkAssignment(BlockScope scope, TypeBinding lhsType, TypeBinding rhsType) { | |
FieldBinding leftField = getLastField(this.lhs); | |
if (leftField != null && rhsType != NullBinding && lhsType.isWildcard() && ((WildcardBinding)lhsType).kind != Wildcard.SUPER) { | |
scope.problemReporter().wildcardAssignment(lhsType, rhsType, this.expression); | |
} else if (leftField != null && leftField.declaringClass != null /*length pseudo field*/&& leftField.declaringClass.isRawType() | |
&& (rhsType.isParameterizedType() || rhsType.isGenericType())) { | |
scope.problemReporter().unsafeRawFieldAssignment(leftField, rhsType, this.lhs); | |
} else if (rhsType.isRawType() && (lhsType.isBoundParameterizedType() || lhsType.isGenericType())) { | |
scope.problemReporter().unsafeRawConversion(this.expression, rhsType, lhsType); | |
} | |
} | |
public void generateCode( | |
BlockScope currentScope, | |
CodeStream codeStream, | |
boolean valueRequired) { | |
// various scenarii are possible, setting an array reference, | |
// a field reference, a blank final field reference, a field of an enclosing instance or | |
// just a local variable. | |
int pc = codeStream.position; | |
if ((this.bits & IsAssignmentWithNoEffectMASK) != 0) { | |
if (valueRequired) { | |
this.expression.generateCode(currentScope, codeStream, true); | |
} | |
} else { | |
((Reference) lhs).generateAssignment(currentScope, codeStream, this, valueRequired); | |
// variable may have been optimized out | |
// the lhs is responsible to perform the implicitConversion generation for the assignment since optimized for unused local assignment. | |
} | |
codeStream.recordPositionsFrom(pc, this.sourceStart); | |
} | |
Binding getDirectBinding(Expression someExpression) { | |
if (someExpression instanceof SingleNameReference) { | |
return ((SingleNameReference)someExpression).binding; | |
} else if (someExpression instanceof FieldReference) { | |
FieldReference fieldRef = (FieldReference)someExpression; | |
if (fieldRef.receiver.isThis() && !(fieldRef.receiver instanceof QualifiedThisReference)) { | |
return fieldRef.binding; | |
} | |
} | |
return null; | |
} | |
FieldBinding getLastField(Expression someExpression) { | |
if (someExpression instanceof SingleNameReference) { | |
if ((someExpression.bits & RestrictiveFlagMASK) == BindingIds.FIELD) { | |
return (FieldBinding) ((SingleNameReference)someExpression).binding; | |
} | |
} else if (someExpression instanceof FieldReference) { | |
return ((FieldReference)someExpression).binding; | |
} else if (someExpression instanceof QualifiedNameReference) { | |
QualifiedNameReference qName = (QualifiedNameReference) someExpression; | |
if (qName.otherBindings == null && ((someExpression.bits & RestrictiveFlagMASK) == BindingIds.FIELD)) { | |
return (FieldBinding)qName.binding; | |
} else { | |
return qName.otherBindings[qName.otherBindings.length - 1]; | |
} | |
} | |
return null; | |
} | |
public StringBuffer print(int indent, StringBuffer output) { | |
//no () when used as a statement | |
printIndent(indent, output); | |
return printExpressionNoParenthesis(indent, output); | |
} | |
public StringBuffer printExpression(int indent, StringBuffer output) { | |
//subclass redefine printExpressionNoParenthesis() | |
output.append('('); | |
return printExpressionNoParenthesis(0, output).append(')'); | |
} | |
public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) { | |
lhs.printExpression(indent, output).append(" = "); //$NON-NLS-1$ | |
return expression.printExpression(0, output); | |
} | |
public StringBuffer printStatement(int indent, StringBuffer output) { | |
//no () when used as a statement | |
return print(indent, output).append(';'); | |
} | |
public TypeBinding resolveType(BlockScope scope) { | |
// due to syntax lhs may be only a NameReference, a FieldReference or an ArrayReference | |
constant = NotAConstant; | |
if (!(this.lhs instanceof Reference) || this.lhs.isThis()) { | |
scope.problemReporter().expressionShouldBeAVariable(this.lhs); | |
return null; | |
} | |
TypeBinding lhsType = this.resolvedType = lhs.resolveType(scope); | |
expression.setExpectedType(lhsType); // needed in case of generic method invocation | |
TypeBinding rhsType = expression.resolveType(scope); | |
if (lhsType == null || rhsType == null) { | |
return null; | |
} | |
checkAssignmentEffect(scope); | |
// Compile-time conversion of base-types : implicit narrowing integer into byte/short/character | |
// may require to widen the rhs expression at runtime | |
if ((expression.isConstantValueOfTypeAssignableToType(rhsType, lhsType) | |
|| (lhsType.isBaseType() && BaseTypeBinding.isWidening(lhsType.id, rhsType.id))) | |
|| rhsType.isCompatibleWith(lhsType)) { | |
expression.computeConversion(scope, lhsType, rhsType); | |
checkAssignment(scope, lhsType, rhsType); | |
return this.resolvedType; | |
} | |
scope.problemReporter().typeMismatchError(rhsType, lhsType, expression); | |
return lhsType; | |
} | |
/* (non-Javadoc) | |
* @see org.eclipse.wst.jsdt.internal.compiler.ast.Expression#resolveTypeExpecting(org.eclipse.wst.jsdt.internal.compiler.lookup.BlockScope, org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding) | |
*/ | |
public TypeBinding resolveTypeExpecting( | |
BlockScope scope, | |
TypeBinding expectedType) { | |
TypeBinding type = super.resolveTypeExpecting(scope, expectedType); | |
if (type == null) return null; | |
TypeBinding lhsType = this.resolvedType; | |
TypeBinding rhsType = this.expression.resolvedType; | |
// signal possible accidental boolean assignment (instead of using '==' operator) | |
if (expectedType == BooleanBinding | |
&& lhsType == BooleanBinding | |
&& (this.lhs.bits & IsStrictlyAssignedMASK) != 0) { | |
scope.problemReporter().possibleAccidentalBooleanAssignment(this); | |
} | |
checkAssignment(scope, lhsType, rhsType); | |
return type; | |
} | |
public void traverse(ASTVisitor visitor, BlockScope scope) { | |
if (visitor.visit(this, scope)) { | |
lhs.traverse(visitor, scope); | |
expression.traverse(visitor, scope); | |
} | |
visitor.endVisit(this, scope); | |
} | |
} |