added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / CastExpression.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  *     Nick Teryaev - fix for bug (https://bugs.eclipse.org/bugs/show_bug.cgi?id=40752)
11  *******************************************************************************/
12 package org.eclipse.jdt.internal.compiler.ast;
13
14 import org.eclipse.jdt.internal.compiler.ASTVisitor;
15 import org.eclipse.jdt.internal.compiler.impl.*;
16 import org.eclipse.jdt.internal.compiler.codegen.*;
17 import org.eclipse.jdt.internal.compiler.flow.*;
18 import org.eclipse.jdt.internal.compiler.lookup.*;
19 import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
20
21 public class CastExpression extends Expression {
22
23         public Expression expression;
24         public Expression type;
25         public TypeBinding expectedType; // when assignment conversion to a given expected type: String s = (String) t;
26         
27         //expression.implicitConversion holds the cast for baseType casting 
28         public CastExpression(Expression expression, Expression type) {
29                 this.expression = expression;
30                 this.type = type;
31
32                 //due to the fact an expression may start with ( and that a cast also start with (
33                 //the field is an expression....it can be a TypeReference OR a NameReference Or
34                 //an expression <--this last one is invalid.......
35
36                 //if (type instanceof TypeReference )
37                 //      flag = IsTypeReference ;
38                 //else
39                 //      if (type instanceof NameReference)
40                 //              flag = IsNameReference ;
41                 //      else
42                 //              flag = IsExpression ;
43
44         }
45
46         public FlowInfo analyseCode(
47                 BlockScope currentScope,
48                 FlowContext flowContext,
49                 FlowInfo flowInfo) {
50
51                 return expression
52                         .analyseCode(currentScope, flowContext, flowInfo)
53                         .unconditionalInits();
54         }
55
56         /**
57          * Casting an enclosing instance will considered as useful if removing it would actually bind to a different type
58          */
59         public static void checkNeedForEnclosingInstanceCast(BlockScope scope, Expression enclosingInstance, TypeBinding enclosingInstanceType, TypeBinding memberType) {
60         
61                 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
62                 
63                 TypeBinding castedExpressionType = ((CastExpression)enclosingInstance).expression.resolvedType;
64                 if (castedExpressionType == null) return; // cannot do better
65                 // obvious identity cast
66                 if (castedExpressionType == enclosingInstanceType) { 
67                         scope.problemReporter().unnecessaryCast((CastExpression)enclosingInstance);
68                 } else if (castedExpressionType == NullBinding){
69                         return; // tolerate null enclosing instance cast
70                 } else {
71                         TypeBinding alternateEnclosingInstanceType = castedExpressionType; 
72                         if (castedExpressionType.isBaseType() || castedExpressionType.isArrayType()) return; // error case
73                         if (memberType == scope.getMemberType(memberType.sourceName(), (ReferenceBinding) alternateEnclosingInstanceType)) {
74                                 scope.problemReporter().unnecessaryCast((CastExpression)enclosingInstance);
75                         }
76                 }
77         }
78
79         /**
80          * Only complain for identity cast, since other type of casts may be useful: e.g.  ~((~(long) 0) << 32)  is different from: ~((~0) << 32) 
81          */
82         public static void checkNeedForArgumentCast(BlockScope scope, int operator, int operatorSignature, Expression expression, int expressionTypeId) {
83         
84                 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
85         
86                 // check need for left operand cast
87                 int alternateLeftTypeId = expressionTypeId;
88                 if ((expression.bits & UnnecessaryCastMask) == 0 && expression.resolvedType.isBaseType()) {
89                         // narrowing conversion on base type may change value, thus necessary
90                         return;
91                 } else  {
92                         TypeBinding alternateLeftType = ((CastExpression)expression).expression.resolvedType;
93                         if (alternateLeftType == null) return; // cannot do better
94                         if ((alternateLeftTypeId = alternateLeftType.id) == expressionTypeId) { // obvious identity cast
95                                 scope.problemReporter().unnecessaryCast((CastExpression)expression); 
96                                 return;
97                         } else if (alternateLeftTypeId == T_null) {
98                                 alternateLeftTypeId = expressionTypeId;  // tolerate null argument cast
99                                 return;
100                         }
101                 }
102 /*              tolerate widening cast in unary expressions, as may be used when combined in binary expressions (41680)
103                 int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4) + alternateLeftTypeId];
104                 // (cast)  left   Op (cast)  right --> result
105                 //  1111   0000       1111   0000     1111
106                 //  <<16   <<12       <<8    <<4       <<0
107                 final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types
108                 if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
109                         scope.problemReporter().unnecessaryCastForArgument((CastExpression)expression,  TypeBinding.wellKnownType(scope, expression.implicitConversion >> 4)); 
110                 }
111 */              
112         }
113                 
114         /**
115          * Cast expressions will considered as useful if removing them all would actually bind to a different method
116          * (no fine grain analysis on per casted argument basis, simply separate widening cast from narrowing ones)
117          */
118         public static void checkNeedForArgumentCasts(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] argumentTypes, final InvocationSite invocationSite) {
119         
120                 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
121                 
122                 int length = argumentTypes.length;
123
124                 // iterate over arguments, and retrieve original argument types (before cast)
125                 TypeBinding[] rawArgumentTypes = argumentTypes;
126                 for (int i = 0; i < length; i++) {
127                         Expression argument = arguments[i];
128                         if (argument instanceof CastExpression) {
129                                 // narrowing conversion on base type may change value, thus necessary
130                                 if ((argument.bits & UnnecessaryCastMask) == 0 && argument.resolvedType.isBaseType()) {
131                                         continue;
132                                 }               
133                                 TypeBinding castedExpressionType = ((CastExpression)argument).expression.resolvedType;
134                                 if (castedExpressionType == null) return; // cannot do better
135                                 // obvious identity cast
136                                 if (castedExpressionType == argumentTypes[i]) { 
137                                         scope.problemReporter().unnecessaryCast((CastExpression)argument);
138                                 } else if (castedExpressionType == NullBinding){
139                                         continue; // tolerate null argument cast
140                                 } else {
141                                         if (rawArgumentTypes == argumentTypes) {
142                                                 System.arraycopy(rawArgumentTypes, 0, rawArgumentTypes = new TypeBinding[length], 0, length);
143                                         }
144                                         // retain original argument type
145                                         rawArgumentTypes[i] = castedExpressionType; 
146                                 }
147                         }                               
148                 }
149                 // perform alternate lookup with original types
150                 if (rawArgumentTypes != argumentTypes) {
151                         checkAlternateBinding(scope, receiver, receiverType, binding, arguments, argumentTypes, rawArgumentTypes, invocationSite);
152                 }
153         }
154
155         /**
156          * Check binary operator casted arguments 
157          */
158         public static void checkNeedForArgumentCasts(BlockScope scope, int operator, int operatorSignature, Expression left, int leftTypeId, boolean leftIsCast, Expression right, int rightTypeId, boolean rightIsCast) {
159
160                 if (scope.environment().options.getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
161
162                 // check need for left operand cast
163                 int alternateLeftTypeId = leftTypeId;
164                 if (leftIsCast) {
165                         if ((left.bits & UnnecessaryCastMask) == 0 && left.resolvedType.isBaseType()) {
166                                 // narrowing conversion on base type may change value, thus necessary
167                                 leftIsCast = false;
168                         } else  {
169                                 TypeBinding alternateLeftType = ((CastExpression)left).expression.resolvedType;
170                                 if (alternateLeftType == null) return; // cannot do better
171                                 if ((alternateLeftTypeId = alternateLeftType.id) == leftTypeId) { // obvious identity cast
172                                         scope.problemReporter().unnecessaryCast((CastExpression)left); 
173                                         leftIsCast = false;
174                                 } else if (alternateLeftTypeId == T_null) {
175                                         alternateLeftTypeId = leftTypeId;  // tolerate null argument cast
176                                         leftIsCast = false;
177                                 }
178                         }
179                 }
180                 // check need for right operand cast
181                 int alternateRightTypeId = rightTypeId;
182                 if (rightIsCast) {
183                         if ((right.bits & UnnecessaryCastMask) == 0 && right.resolvedType.isBaseType()) {
184                                 // narrowing conversion on base type may change value, thus necessary
185                                 rightIsCast = false;
186                         } else {
187                                 TypeBinding alternateRightType = ((CastExpression)right).expression.resolvedType;
188                                 if (alternateRightType == null) return; // cannot do better
189                                 if ((alternateRightTypeId = alternateRightType.id) == rightTypeId) { // obvious identity cast
190                                         scope.problemReporter().unnecessaryCast((CastExpression)right); 
191                                         rightIsCast = false;
192                                 } else if (alternateRightTypeId == T_null) {
193                                         alternateRightTypeId = rightTypeId;  // tolerate null argument cast
194                                         rightIsCast = false;
195                                 }
196                         }       
197                 }
198                 if (leftIsCast || rightIsCast) {
199                         if (alternateLeftTypeId > 15 || alternateRightTypeId > 15) { // must convert String + Object || Object + String
200                                 if (alternateLeftTypeId == T_JavaLangString) {
201                                         alternateRightTypeId = T_JavaLangObject;
202                                 } else if (alternateRightTypeId == T_JavaLangString) {
203                                         alternateLeftTypeId = T_JavaLangObject;
204                                 } else {
205                                         return; // invalid operator
206                                 }
207                         }
208                         int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4) + alternateRightTypeId];
209                         // (cast)  left   Op (cast)  right --> result
210                         //  1111   0000       1111   0000     1111
211                         //  <<16   <<12       <<8    <<4       <<0
212                         final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types
213                         if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
214                                 if (leftIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)left,  TypeBinding.wellKnownType(scope, (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4)); 
215                                 if (rightIsCast) scope.problemReporter().unnecessaryCastForArgument((CastExpression)right, TypeBinding.wellKnownType(scope,  (right.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4));
216                         }
217                 }
218         }
219
220         private static void checkAlternateBinding(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) {
221
222                         InvocationSite fakeInvocationSite = new InvocationSite(){       
223                                 public TypeBinding[] genericTypeArguments() { return null; }
224                                 public boolean isSuperAccess(){ return invocationSite.isSuperAccess(); }
225                                 public boolean isTypeAccess() { return invocationSite.isTypeAccess(); }
226                                 public void setActualReceiverType(ReferenceBinding actualReceiverType) { /* ignore */}
227                                 public void setDepth(int depth) { /* ignore */}
228                                 public void setFieldIndex(int depth){ /* ignore */}
229                                 public int sourceStart() { return 0; }
230                                 public int sourceEnd() { return 0; }
231                         };      
232                         MethodBinding bindingIfNoCast;
233                         if (binding.isConstructor()) {
234                                 bindingIfNoCast = scope.getConstructor((ReferenceBinding)receiverType, alternateArgumentTypes, fakeInvocationSite);
235                         } else {
236                                 bindingIfNoCast = receiver.isImplicitThis()
237                                         ? scope.getImplicitMethod(binding.selector, alternateArgumentTypes, fakeInvocationSite)
238                                         : scope.getMethod(receiverType, binding.selector, alternateArgumentTypes, fakeInvocationSite);  
239                         }
240                         if (bindingIfNoCast == binding) {
241                                 for (int i = 0, length = originalArgumentTypes.length; i < length; i++) {
242                                         if (originalArgumentTypes[i] != alternateArgumentTypes[i]) {
243                                                 scope.problemReporter().unnecessaryCastForArgument((CastExpression)arguments[i], binding.parameters[i]);
244                                         }
245                                 }
246                         }       
247         }
248         
249         public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) {
250                 if (match == castType) {
251                         if (!isNarrowing) tagAsUnnecessaryCast(scope, castType);
252                         return true;
253                 }
254                 if (castType.isBoundParameterizedType() || castType.isGenericType()) {
255                         if (match.isProvablyDistinctFrom(isNarrowing ? expressionType : castType, 0)) {
256                                 reportIllegalCast(scope, castType, expressionType);
257                                 return false; 
258                         }
259                         if (isNarrowing ? !expressionType.isEquivalentTo(match) : !match.isEquivalentTo(castType)) {
260                                 scope.problemReporter().unsafeCast(this);
261                                 return true;
262                         }
263                         if ((castType.tagBits & TagBits.HasDirectWildcard) == 0) {
264                                 if ((!match.isParameterizedType() && !match.isGenericType())
265                                                 || expressionType.isRawType()) {
266                                         scope.problemReporter().unsafeCast(this);
267                                         return true;
268                                 }
269                         }
270                 }
271                 if (!isNarrowing) tagAsUnnecessaryCast(scope, castType);
272                 return true;
273         }       
274         
275         /**
276          * Cast expression code generation
277          *
278          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
279          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
280          * @param valueRequired boolean
281          */
282         public void generateCode(
283                 BlockScope currentScope,
284                 CodeStream codeStream,
285                 boolean valueRequired) {
286         
287                 int pc = codeStream.position;
288                 boolean needRuntimeCheckcast = (this.bits & NeedRuntimeCheckCastMASK) != 0;
289                 if (constant != NotAConstant) {
290                         if (valueRequired || needRuntimeCheckcast) { // Added for: 1F1W9IG: IVJCOM:WINNT - Compiler omits casting check
291                                 codeStream.generateConstant(constant, implicitConversion);
292                                 if (needRuntimeCheckcast) {
293                                         codeStream.checkcast(this.resolvedType);
294                                         if (valueRequired) {
295                                                 codeStream.generateImplicitConversion(this.implicitConversion);
296                                         } else {
297                                                 codeStream.pop();
298                                         }
299                                 }
300                         }
301                         codeStream.recordPositionsFrom(pc, this.sourceStart);
302                         return;
303                 }
304                 expression.generateCode(
305                         currentScope,
306                         codeStream,
307                         valueRequired || needRuntimeCheckcast);
308                 if (needRuntimeCheckcast) {
309                         codeStream.checkcast(this.resolvedType);
310                         if (valueRequired) {
311                                 codeStream.generateImplicitConversion(implicitConversion);
312                         } else {
313                                 codeStream.pop();
314                         }
315                 } else {
316                         if (valueRequired)
317                                 codeStream.generateImplicitConversion(implicitConversion);
318                 }
319                 codeStream.recordPositionsFrom(pc, this.sourceStart);
320         }
321
322         public Expression innermostCastedExpression(){ 
323                 Expression current = this.expression;
324                 while (current instanceof CastExpression) {
325                         current = ((CastExpression) current).expression;
326                 }
327                 return current;
328         }
329
330         /**
331          * @see org.eclipse.jdt.internal.compiler.ast.Expression#localVariableBinding()
332          */
333         public LocalVariableBinding localVariableBinding() {
334                 return this.expression.localVariableBinding();
335         }
336         
337         public int nullStatus(FlowInfo flowInfo) {
338                 return this.expression.nullStatus(flowInfo);
339         }
340         
341         public StringBuffer printExpression(int indent, StringBuffer output) {
342
343                 output.append('(');
344                 type.print(0, output).append(") "); //$NON-NLS-1$
345                 return expression.printExpression(0, output);
346         }
347
348         public void reportIllegalCast(Scope scope, TypeBinding castType, TypeBinding expressionType) {
349                 scope.problemReporter().typeCastError(this, castType, expressionType);
350         }
351         
352         public TypeBinding resolveType(BlockScope scope) {
353                 // compute a new constant if the cast is effective
354
355                 // due to the fact an expression may start with ( and that a cast can also start with (
356                 // the field is an expression....it can be a TypeReference OR a NameReference Or
357                 // any kind of Expression <-- this last one is invalid.......
358
359                 constant = Constant.NotAConstant;
360                 implicitConversion = T_undefined;
361
362                 if ((type instanceof TypeReference) || (type instanceof NameReference)
363                                 && ((type.bits & ASTNode.ParenthesizedMASK) >> ASTNode.ParenthesizedSHIFT) == 0) { // no extra parenthesis around type: ((A))exp
364
365                         this.resolvedType = type.resolveType(scope);
366                         expression.setExpectedType(this.resolvedType); // needed in case of generic method invocation                   
367                         TypeBinding expressionType = expression.resolveType(scope);
368                         if (this.resolvedType != null && expressionType != null) {
369                                 checkCastTypesCompatibility(scope, this.resolvedType, expressionType, this.expression);
370                                 this.expression.computeConversion(scope, this.resolvedType, expressionType);
371                                 if ((this.bits & UnnecessaryCastMask) != 0) {
372                                         if ((this.bits & IgnoreNeedForCastCheckMASK) == 0) {
373                                                 if (!usedForGenericMethodReturnTypeInference()) // used for generic type inference ?
374                                                         scope.problemReporter().unnecessaryCast(this);
375                                         }
376                                 }
377                         }
378                         return this.resolvedType;
379                 } else { // expression as a cast
380                         TypeBinding expressionType = expression.resolveType(scope);
381                         if (expressionType == null) return null;
382                         scope.problemReporter().invalidTypeReference(type);
383                         return null;
384                 }
385         }
386         
387         /**
388          * @see org.eclipse.jdt.internal.compiler.ast.Expression#setExpectedType(org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
389          */
390         public void setExpectedType(TypeBinding expectedType) {
391                 this.expectedType = expectedType;
392         }
393
394         /**
395          * Determines whether apparent unnecessary cast wasn't actually used to
396          * perform return type inference of generic method invocation.
397          */
398         private boolean usedForGenericMethodReturnTypeInference() {
399                 if (this.expression instanceof MessageSend) {
400                         MethodBinding method = ((MessageSend)this.expression).binding;
401                         if (method instanceof ParameterizedGenericMethodBinding
402                                                 && ((ParameterizedGenericMethodBinding)method).inferredReturnType) {
403                                 if (this.expectedType == null) 
404                                         return true;
405                                 if (this.resolvedType != this.expectedType)
406                                         return true;
407                         }
408                 }
409                 return false;
410         }
411
412         /**
413          * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsNeedCheckCast()
414          */
415         public void tagAsNeedCheckCast() {
416                 this.bits |= NeedRuntimeCheckCastMASK;
417         }
418         
419         /**
420          * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsUnnecessaryCast(Scope, TypeBinding)
421          */
422         public void tagAsUnnecessaryCast(Scope scope, TypeBinding castType) {
423                 if (this.expression.resolvedType == null) return; // cannot do better if expression is not bound
424                 this.bits |= UnnecessaryCastMask;
425         }
426         
427         public void traverse(
428                 ASTVisitor visitor,
429                 BlockScope blockScope) {
430
431                 if (visitor.visit(this, blockScope)) {
432                         type.traverse(visitor, blockScope);
433                         expression.traverse(visitor, blockScope);
434                 }
435                 visitor.endVisit(this, blockScope);
436         }
437 }