/******************************************************************************* | |
* 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 | |
*******************************************************************************/ | |
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 CompoundAssignment extends Assignment implements OperatorIds { | |
public int operator; | |
public int assignmentImplicitConversion; | |
// var op exp is equivalent to var = (varType) var op exp | |
// assignmentImplicitConversion stores the cast needed for the assignment | |
public CompoundAssignment(Expression lhs, Expression expression,int operator, int sourceEnd) { | |
//lhs is always a reference by construction , | |
//but is build as an expression ==> the checkcast cannot fail | |
super(lhs, expression, sourceEnd); | |
lhs.bits &= ~IsStrictlyAssignedMASK; // tag lhs as NON assigned - it is also a read access | |
lhs.bits |= IsCompoundAssignedMASK; // tag lhs as assigned by compound | |
this.operator = operator ; | |
} | |
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, true).unconditionalInits(); | |
} | |
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; | |
((Reference) lhs).generateCompoundAssignment(currentScope, codeStream, expression, operator, assignmentImplicitConversion, valueRequired); | |
if (valueRequired) { | |
codeStream.generateImplicitConversion(implicitConversion); | |
} | |
codeStream.recordPositionsFrom(pc, this.sourceStart); | |
} | |
public String operatorToString() { | |
switch (operator) { | |
case PLUS : | |
return "+="; //$NON-NLS-1$ | |
case MINUS : | |
return "-="; //$NON-NLS-1$ | |
case MULTIPLY : | |
return "*="; //$NON-NLS-1$ | |
case DIVIDE : | |
return "/="; //$NON-NLS-1$ | |
case AND : | |
return "&="; //$NON-NLS-1$ | |
case OR : | |
return "|="; //$NON-NLS-1$ | |
case XOR : | |
return "^="; //$NON-NLS-1$ | |
case REMAINDER : | |
return "%="; //$NON-NLS-1$ | |
case LEFT_SHIFT : | |
return "<<="; //$NON-NLS-1$ | |
case RIGHT_SHIFT : | |
return ">>="; //$NON-NLS-1$ | |
case UNSIGNED_RIGHT_SHIFT : | |
return ">>>="; //$NON-NLS-1$ | |
} | |
return "unknown operator"; //$NON-NLS-1$ | |
} | |
public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) { | |
lhs.printExpression(indent, output).append(' ').append(operatorToString()).append(' '); | |
return expression.printExpression(0, output) ; | |
} | |
public TypeBinding resolveType(BlockScope scope) { | |
constant = NotAConstant; | |
if (!(this.lhs instanceof Reference) || this.lhs.isThis()) { | |
scope.problemReporter().expressionShouldBeAVariable(this.lhs); | |
return null; | |
} | |
TypeBinding lhsType = lhs.resolveType(scope); | |
TypeBinding expressionType = expression.resolveType(scope); | |
if (lhsType == null || expressionType == null) | |
return null; | |
int lhsId = lhsType.id; | |
int expressionId = expressionType.id; | |
if (restrainUsageToNumericTypes() && !lhsType.isNumericType()) { | |
scope.problemReporter().operatorOnlyValidOnNumericType(this, lhsType, expressionType); | |
return null; | |
} | |
if (lhsId > 15 || expressionId > 15) { | |
if (lhsId != T_String) { // String += Thread is valid whereas Thread += String is not | |
scope.problemReporter().invalidOperator(this, lhsType, expressionType); | |
return null; | |
} | |
expressionId = T_Object; // use the Object has tag table | |
} | |
// the code is an int | |
// (cast) left Op (cast) rigth --> result | |
// 0000 0000 0000 0000 0000 | |
// <<16 <<12 <<8 <<4 <<0 | |
// the conversion is stored INTO the reference (info needed for the code gen) | |
int result = OperatorExpression.OperatorSignatures[operator][ (lhsId << 4) + expressionId]; | |
if (result == T_undefined) { | |
scope.problemReporter().invalidOperator(this, lhsType, expressionType); | |
return null; | |
} | |
if (operator == PLUS){ | |
if(lhsId == T_JavaLangObject) { | |
// <Object> += <String> is illegal (39248) | |
scope.problemReporter().invalidOperator(this, lhsType, expressionType); | |
return null; | |
} else { | |
// <int | boolean> += <String> is illegal | |
if ((lhsType.isNumericType() || lhsId == T_boolean) && !expressionType.isNumericType()){ | |
scope.problemReporter().invalidOperator(this, lhsType, expressionType); | |
return null; | |
} | |
} | |
} | |
// TODO (philippe) should retrofit in using #computeConversion | |
lhs.implicitConversion = result >>> 12; | |
expression.implicitConversion = (result >>> 4) & 0x000FF; | |
assignmentImplicitConversion = (lhsId << 4) + (result & 0x0000F); | |
return this.resolvedType = lhsType; | |
} | |
public boolean restrainUsageToNumericTypes(){ | |
return false ; | |
} | |
public void traverse(ASTVisitor visitor, BlockScope scope) { | |
if (visitor.visit(this, scope)) { | |
lhs.traverse(visitor, scope); | |
expression.traverse(visitor, scope); | |
} | |
visitor.endVisit(this, scope); | |
} | |
} |