| /******************************************************************************* |
| * 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.jdt.internal.compiler.ast; |
| |
| import org.eclipse.jdt.internal.compiler.ASTVisitor; |
| import org.eclipse.jdt.internal.compiler.codegen.*; |
| import org.eclipse.jdt.internal.compiler.flow.*; |
| import org.eclipse.jdt.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); |
| } |
| } |