added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / CompoundAssignment.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
12
13 import org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.codegen.*;
15 import org.eclipse.jdt.internal.compiler.flow.*;
16 import org.eclipse.jdt.internal.compiler.lookup.*;
17
18 public class CompoundAssignment extends Assignment implements OperatorIds {
19         public int operator;
20         public int assignmentImplicitConversion;
21
22         //  var op exp is equivalent to var = (varType) var op exp
23         // assignmentImplicitConversion stores the cast needed for the assignment
24
25         public CompoundAssignment(Expression lhs, Expression expression,int operator, int sourceEnd) {
26                 //lhs is always a reference by construction ,
27                 //but is build as an expression ==> the checkcast cannot fail
28         
29                 super(lhs, expression, sourceEnd);
30                 lhs.bits &= ~IsStrictlyAssignedMASK; // tag lhs as NON assigned - it is also a read access
31                 lhs.bits |= IsCompoundAssignedMASK; // tag lhs as assigned by compound
32                 this.operator = operator ;
33         }
34         
35         public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
36                 // record setting a variable: various scenarii are possible, setting an array reference, 
37                 // a field reference, a blank final field reference, a field of an enclosing instance or 
38                 // just a local variable.
39         
40                 return  ((Reference) lhs).analyseAssignment(currentScope, flowContext, flowInfo, this, true).unconditionalInits();
41         }
42         
43         public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
44         
45                 // various scenarii are possible, setting an array reference, 
46                 // a field reference, a blank final field reference, a field of an enclosing instance or 
47                 // just a local variable.
48         
49                 int pc = codeStream.position;
50                  ((Reference) lhs).generateCompoundAssignment(currentScope, codeStream, expression, operator, assignmentImplicitConversion, valueRequired);
51                 if (valueRequired) {
52                         codeStream.generateImplicitConversion(implicitConversion);
53                 }
54                 codeStream.recordPositionsFrom(pc, this.sourceStart);
55         }
56         
57         public int nullStatus(FlowInfo flowInfo) {
58                 return FlowInfo.NON_NULL;
59         }
60         
61         public String operatorToString() {
62                 switch (operator) {
63                         case PLUS :
64                                 return "+="; //$NON-NLS-1$
65                         case MINUS :
66                                 return "-="; //$NON-NLS-1$
67                         case MULTIPLY :
68                                 return "*="; //$NON-NLS-1$
69                         case DIVIDE :
70                                 return "/="; //$NON-NLS-1$
71                         case AND :
72                                 return "&="; //$NON-NLS-1$
73                         case OR :
74                                 return "|="; //$NON-NLS-1$
75                         case XOR :
76                                 return "^="; //$NON-NLS-1$
77                         case REMAINDER :
78                                 return "%="; //$NON-NLS-1$
79                         case LEFT_SHIFT :
80                                 return "<<="; //$NON-NLS-1$
81                         case RIGHT_SHIFT :
82                                 return ">>="; //$NON-NLS-1$
83                         case UNSIGNED_RIGHT_SHIFT :
84                                 return ">>>="; //$NON-NLS-1$
85                 }
86                 return "unknown operator"; //$NON-NLS-1$
87         }
88         
89         public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
90         
91                 lhs.printExpression(indent, output).append(' ').append(operatorToString()).append(' ');
92                 return expression.printExpression(0, output) ; 
93         }
94         
95         public TypeBinding resolveType(BlockScope scope) {
96                 constant = NotAConstant;
97                 if (!(this.lhs instanceof Reference) || this.lhs.isThis()) {
98                         scope.problemReporter().expressionShouldBeAVariable(this.lhs);
99                         return null;
100                 }
101                 TypeBinding originalLhsType = lhs.resolveType(scope);
102                 TypeBinding originalExpressionType = expression.resolveType(scope);
103                 if (originalLhsType == null || originalExpressionType == null)
104                         return null;
105         
106                 // autoboxing support
107                 LookupEnvironment env = scope.environment();
108                 TypeBinding lhsType = originalLhsType, expressionType = originalExpressionType;
109                 boolean use15specifics = scope.environment().options.sourceLevel >= JDK1_5;
110                 boolean unboxedLhs = false;
111                 if (use15specifics) {
112                         if (!lhsType.isBaseType() && expressionType.id != T_JavaLangString && expressionType.id != T_null) {
113                                 TypeBinding unboxedType = env.computeBoxingType(lhsType);
114                                 if (unboxedType != lhsType) {
115                                         lhsType = unboxedType;
116                                         unboxedLhs = true;
117                                 }
118                         }
119                         if (!expressionType.isBaseType() && lhsType.id != T_JavaLangString  && lhsType.id != T_null) {
120                                 expressionType = env.computeBoxingType(expressionType);
121                         }
122                 }
123                 
124                 if (restrainUsageToNumericTypes() && !lhsType.isNumericType()) {
125                         scope.problemReporter().operatorOnlyValidOnNumericType(this, lhsType, expressionType);
126                         return null;
127                 }
128                 int lhsID = lhsType.id;
129                 int expressionID = expressionType.id;
130                 if (lhsID > 15 || expressionID > 15) {
131                         if (lhsID != T_JavaLangString) { // String += Thread is valid whereas Thread += String  is not
132                                 scope.problemReporter().invalidOperator(this, lhsType, expressionType);
133                                 return null;
134                         }
135                         expressionID = T_JavaLangObject; // use the Object has tag table
136                 }
137         
138                 // the code is an int
139                 // (cast)  left   Op (cast)  rigth --> result 
140                 //  0000   0000       0000   0000      0000
141                 //  <<16   <<12       <<8     <<4        <<0
142         
143                 // the conversion is stored INTO the reference (info needed for the code gen)
144                 int result = OperatorExpression.OperatorSignatures[operator][ (lhsID << 4) + expressionID];
145                 if (result == T_undefined) {
146                         scope.problemReporter().invalidOperator(this, lhsType, expressionType);
147                         return null;
148                 }
149                 if (operator == PLUS){
150                         if(lhsID == T_JavaLangObject) {
151                                 // <Object> += <String> is illegal (39248)
152                                 scope.problemReporter().invalidOperator(this, lhsType, expressionType);
153                                 return null;
154                         } else {
155                                 // <int | boolean> += <String> is illegal
156                                 if ((lhsType.isNumericType() || lhsID == T_boolean) && !expressionType.isNumericType()){
157                                         scope.problemReporter().invalidOperator(this, lhsType, expressionType);
158                                         return null;
159                                 }
160                         }
161                 }
162                 this.lhs.implicitConversion = (unboxedLhs ? UNBOXING : 0) | (result >>> 12);
163                 this.expression.computeConversion(scope, TypeBinding.wellKnownType(scope, (result >>> 8) & 0x0000F), originalExpressionType);
164                 this.assignmentImplicitConversion =  (unboxedLhs ? BOXING : 0) | (lhsID << 4) | (result & 0x0000F);
165                 return this.resolvedType = originalLhsType;
166         }
167         
168         public boolean restrainUsageToNumericTypes(){
169                 return false ;
170         }
171         
172         public void traverse(ASTVisitor visitor, BlockScope scope) {
173                 if (visitor.visit(this, scope)) {
174                         lhs.traverse(visitor, scope);
175                         expression.traverse(visitor, scope);
176                 }
177                 visitor.endVisit(this, scope);
178         }
179 }