001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.compiler.ast;
011:
012: import org.eclipse.jdt.internal.compiler.ASTVisitor;
013: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
014: import org.eclipse.jdt.internal.compiler.codegen.*;
015: import org.eclipse.jdt.internal.compiler.flow.*;
016: import org.eclipse.jdt.internal.compiler.impl.Constant;
017: import org.eclipse.jdt.internal.compiler.lookup.*;
018:
019: public class CompoundAssignment extends Assignment implements
020: OperatorIds {
021: public int operator;
022: public int preAssignImplicitConversion;
023:
024: // var op exp is equivalent to var = (varType) var op exp
025: // assignmentImplicitConversion stores the cast needed for the assignment
026:
027: public CompoundAssignment(Expression lhs, Expression expression,
028: int operator, int sourceEnd) {
029: //lhs is always a reference by construction ,
030: //but is build as an expression ==> the checkcast cannot fail
031:
032: super (lhs, expression, sourceEnd);
033: lhs.bits &= ~IsStrictlyAssigned; // tag lhs as NON assigned - it is also a read access
034: lhs.bits |= IsCompoundAssigned; // tag lhs as assigned by compound
035: this .operator = operator;
036: }
037:
038: public FlowInfo analyseCode(BlockScope currentScope,
039: FlowContext flowContext, FlowInfo flowInfo) {
040: // record setting a variable: various scenarii are possible, setting an array reference,
041: // a field reference, a blank final field reference, a field of an enclosing instance or
042: // just a local variable.
043: if (this .resolvedType.id != T_JavaLangString) {
044: lhs.checkNPE(currentScope, flowContext, flowInfo);
045: }
046: return ((Reference) lhs).analyseAssignment(currentScope,
047: flowContext, flowInfo, this , true).unconditionalInits();
048: }
049:
050: public void generateCode(BlockScope currentScope,
051: CodeStream codeStream, boolean valueRequired) {
052:
053: // various scenarii are possible, setting an array reference,
054: // a field reference, a blank final field reference, a field of an enclosing instance or
055: // just a local variable.
056:
057: int pc = codeStream.position;
058: ((Reference) lhs).generateCompoundAssignment(currentScope,
059: codeStream, this .expression, this .operator,
060: this .preAssignImplicitConversion, valueRequired);
061: if (valueRequired) {
062: codeStream
063: .generateImplicitConversion(this .implicitConversion);
064: }
065: codeStream.recordPositionsFrom(pc, this .sourceStart);
066: }
067:
068: public int nullStatus(FlowInfo flowInfo) {
069: return FlowInfo.NON_NULL;
070: // we may have complained on checkNPE, but we avoid duplicate error
071: }
072:
073: public String operatorToString() {
074: switch (operator) {
075: case PLUS:
076: return "+="; //$NON-NLS-1$
077: case MINUS:
078: return "-="; //$NON-NLS-1$
079: case MULTIPLY:
080: return "*="; //$NON-NLS-1$
081: case DIVIDE:
082: return "/="; //$NON-NLS-1$
083: case AND:
084: return "&="; //$NON-NLS-1$
085: case OR:
086: return "|="; //$NON-NLS-1$
087: case XOR:
088: return "^="; //$NON-NLS-1$
089: case REMAINDER:
090: return "%="; //$NON-NLS-1$
091: case LEFT_SHIFT:
092: return "<<="; //$NON-NLS-1$
093: case RIGHT_SHIFT:
094: return ">>="; //$NON-NLS-1$
095: case UNSIGNED_RIGHT_SHIFT:
096: return ">>>="; //$NON-NLS-1$
097: }
098: return "unknown operator"; //$NON-NLS-1$
099: }
100:
101: public StringBuffer printExpressionNoParenthesis(int indent,
102: StringBuffer output) {
103:
104: lhs.printExpression(indent, output).append(' ').append(
105: operatorToString()).append(' ');
106: return expression.printExpression(0, output);
107: }
108:
109: public TypeBinding resolveType(BlockScope scope) {
110: constant = Constant.NotAConstant;
111: if (!(this .lhs instanceof Reference) || this .lhs.isThis()) {
112: scope.problemReporter().expressionShouldBeAVariable(
113: this .lhs);
114: return null;
115: }
116: TypeBinding originalLhsType = lhs.resolveType(scope);
117: TypeBinding originalExpressionType = expression
118: .resolveType(scope);
119: if (originalLhsType == null || originalExpressionType == null)
120: return null;
121:
122: // autoboxing support
123: LookupEnvironment env = scope.environment();
124: TypeBinding lhsType = originalLhsType, expressionType = originalExpressionType;
125: boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
126: boolean unboxedLhs = false;
127: if (use15specifics) {
128: if (!lhsType.isBaseType()
129: && expressionType.id != T_JavaLangString
130: && expressionType.id != T_null) {
131: TypeBinding unboxedType = env
132: .computeBoxingType(lhsType);
133: if (unboxedType != lhsType) {
134: lhsType = unboxedType;
135: unboxedLhs = true;
136: }
137: }
138: if (!expressionType.isBaseType()
139: && lhsType.id != T_JavaLangString
140: && lhsType.id != T_null) {
141: expressionType = env.computeBoxingType(expressionType);
142: }
143: }
144:
145: if (restrainUsageToNumericTypes() && !lhsType.isNumericType()) {
146: scope.problemReporter().operatorOnlyValidOnNumericType(
147: this , lhsType, expressionType);
148: return null;
149: }
150: int lhsID = lhsType.id;
151: int expressionID = expressionType.id;
152: if (lhsID > 15 || expressionID > 15) {
153: if (lhsID != T_JavaLangString) { // String += Thread is valid whereas Thread += String is not
154: scope.problemReporter().invalidOperator(this , lhsType,
155: expressionType);
156: return null;
157: }
158: expressionID = T_JavaLangObject; // use the Object has tag table
159: }
160:
161: // the code is an int
162: // (cast) left Op (cast) rigth --> result
163: // 0000 0000 0000 0000 0000
164: // <<16 <<12 <<8 <<4 <<0
165:
166: // the conversion is stored INTO the reference (info needed for the code gen)
167: int result = OperatorExpression.OperatorSignatures[operator][(lhsID << 4)
168: + expressionID];
169: if (result == T_undefined) {
170: scope.problemReporter().invalidOperator(this , lhsType,
171: expressionType);
172: return null;
173: }
174: if (operator == PLUS) {
175: if (lhsID == T_JavaLangObject) {
176: // <Object> += <String> is illegal (39248)
177: scope.problemReporter().invalidOperator(this , lhsType,
178: expressionType);
179: return null;
180: } else {
181: // <int | boolean> += <String> is illegal
182: if ((lhsType.isNumericType() || lhsID == T_boolean)
183: && !expressionType.isNumericType()) {
184: scope.problemReporter().invalidOperator(this ,
185: lhsType, expressionType);
186: return null;
187: }
188: }
189: }
190: this .lhs.computeConversion(scope, TypeBinding.wellKnownType(
191: scope, (result >>> 16) & 0x0000F), originalLhsType);
192: this .expression.computeConversion(scope, TypeBinding
193: .wellKnownType(scope, (result >>> 8) & 0x0000F),
194: originalExpressionType);
195: this .preAssignImplicitConversion = (unboxedLhs ? BOXING : 0)
196: | (lhsID << 4) | (result & 0x0000F);
197: if (unboxedLhs)
198: scope.problemReporter().autoboxing(this , lhsType,
199: originalLhsType);
200: return this .resolvedType = originalLhsType;
201: }
202:
203: public boolean restrainUsageToNumericTypes() {
204: return false;
205: }
206:
207: public void traverse(ASTVisitor visitor, BlockScope scope) {
208: if (visitor.visit(this, scope)) {
209: lhs.traverse(visitor, scope);
210: expression.traverse(visitor, scope);
211: }
212: visitor.endVisit(this, scope);
213: }
214: }
|