KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > compiler > ast > CastExpression


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-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         type.bits |= IgnoreRawTypeCheck; // no need to worry about raw type usage
32
}
33
34     public FlowInfo analyseCode(
35         BlockScope currentScope,
36         FlowContext flowContext,
37         FlowInfo flowInfo) {
38
39         return expression
40             .analyseCode(currentScope, flowContext, flowInfo)
41             .unconditionalInits();
42     }
43
44     /**
45      * Complain if assigned expression is cast, but not actually used as such, e.g. Object o = (List) object;
46      */

47     public static void checkNeedForAssignedCast(BlockScope scope, TypeBinding expectedType, CastExpression rhs) {
48     
49         if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
50     
51         TypeBinding castedExpressionType = rhs.expression.resolvedType;
52         // int i = (byte) n; // cast still had side effect
53
// double d = (float) n; // cast to float is unnecessary
54
if (castedExpressionType == null || rhs.resolvedType.isBaseType()) return;
55         //if (castedExpressionType.id == T_null) return; // tolerate null expression cast
56
if (castedExpressionType.isCompatibleWith(expectedType)) {
57             scope.problemReporter().unnecessaryCast(rhs);
58         }
59     }
60     
61     /**
62      * Casting an enclosing instance will considered as useful if removing it would actually bind to a different type
63      */

64     public static void checkNeedForEnclosingInstanceCast(BlockScope scope, Expression enclosingInstance, TypeBinding enclosingInstanceType, TypeBinding memberType) {
65     
66         if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
67         
68         TypeBinding castedExpressionType = ((CastExpression)enclosingInstance).expression.resolvedType;
69         if (castedExpressionType == null) return; // cannot do better
70
// obvious identity cast
71
if (castedExpressionType == enclosingInstanceType) {
72             scope.problemReporter().unnecessaryCast((CastExpression)enclosingInstance);
73         } else if (castedExpressionType == TypeBinding.NULL){
74             return; // tolerate null enclosing instance cast
75
} else {
76             TypeBinding alternateEnclosingInstanceType = castedExpressionType;
77             if (castedExpressionType.isBaseType() || castedExpressionType.isArrayType()) return; // error case
78
if (memberType == scope.getMemberType(memberType.sourceName(), (ReferenceBinding) alternateEnclosingInstanceType)) {
79                 scope.problemReporter().unnecessaryCast((CastExpression)enclosingInstance);
80             }
81         }
82     }
83
84     /**
85      * Only complain for identity cast, since other type of casts may be useful: e.g. ~((~(long) 0) << 32) is different from: ~((~0) << 32)
86      */

87     public static void checkNeedForArgumentCast(BlockScope scope, int operator, int operatorSignature, Expression expression, int expressionTypeId) {
88     
89         if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
90     
91         // check need for left operand cast
92
int alternateLeftTypeId = expressionTypeId;
93         if ((expression.bits & UnnecessaryCast) == 0 && expression.resolvedType.isBaseType()) {
94             // narrowing conversion on base type may change value, thus necessary
95
return;
96         } else {
97             TypeBinding alternateLeftType = ((CastExpression)expression).expression.resolvedType;
98             if (alternateLeftType == null) return; // cannot do better
99
if ((alternateLeftTypeId = alternateLeftType.id) == expressionTypeId) { // obvious identity cast
100
scope.problemReporter().unnecessaryCast((CastExpression)expression);
101                 return;
102             } else if (alternateLeftTypeId == T_null) {
103                 alternateLeftTypeId = expressionTypeId; // tolerate null argument cast
104
return;
105             }
106         }
107 /* tolerate widening cast in unary expressions, as may be used when combined in binary expressions (41680)
108         int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4) + alternateLeftTypeId];
109         // (cast) left Op (cast) right --> result
110         // 1111 0000 1111 0000 1111
111         // <<16 <<12 <<8 <<4 <<0
112         final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types
113         if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
114             scope.problemReporter().unnecessaryCastForArgument((CastExpression)expression, TypeBinding.wellKnownType(scope, expression.implicitConversion >> 4));
115         }
116 */

117     }
118         
119     /**
120      * Cast expressions will considered as useful if removing them all would actually bind to a different method
121      * (no fine grain analysis on per casted argument basis, simply separate widening cast from narrowing ones)
122      */

123     public static void checkNeedForArgumentCasts(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] argumentTypes, final InvocationSite invocationSite) {
124     
125         if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
126         
127         int length = argumentTypes.length;
128
129         // iterate over arguments, and retrieve original argument types (before cast)
130
TypeBinding[] rawArgumentTypes = argumentTypes;
131         for (int i = 0; i < length; i++) {
132             Expression argument = arguments[i];
133             if (argument instanceof CastExpression) {
134                 // narrowing conversion on base type may change value, thus necessary
135
if ((argument.bits & UnnecessaryCast) == 0 && argument.resolvedType.isBaseType()) {
136                     continue;
137                 }
138                 TypeBinding castedExpressionType = ((CastExpression)argument).expression.resolvedType;
139                 if (castedExpressionType == null) return; // cannot do better
140
// obvious identity cast
141
if (castedExpressionType == argumentTypes[i]) {
142                     scope.problemReporter().unnecessaryCast((CastExpression)argument);
143                 } else if (castedExpressionType == TypeBinding.NULL){
144                     continue; // tolerate null argument cast
145
} else if ((argument.implicitConversion & BOXING) != 0) {
146                     continue; // boxing has a side effect: (int) char is not boxed as simple char
147
} else {
148                     if (rawArgumentTypes == argumentTypes) {
149                         System.arraycopy(rawArgumentTypes, 0, rawArgumentTypes = new TypeBinding[length], 0, length);
150                     }
151                     // retain original argument type
152
rawArgumentTypes[i] = castedExpressionType;
153                 }
154             }
155         }
156         // perform alternate lookup with original types
157
if (rawArgumentTypes != argumentTypes) {
158             checkAlternateBinding(scope, receiver, receiverType, binding, arguments, argumentTypes, rawArgumentTypes, invocationSite);
159         }
160     }
161
162     /**
163      * Check binary operator casted arguments
164      */

165     public static void checkNeedForArgumentCasts(BlockScope scope, int operator, int operatorSignature, Expression left, int leftTypeId, boolean leftIsCast, Expression right, int rightTypeId, boolean rightIsCast) {
166
167         if (scope.compilerOptions().getSeverity(CompilerOptions.UnnecessaryTypeCheck) == ProblemSeverities.Ignore) return;
168
169         // check need for left operand cast
170
int alternateLeftTypeId = leftTypeId;
171         if (leftIsCast) {
172             if ((left.bits & UnnecessaryCast) == 0 && left.resolvedType.isBaseType()) {
173                 // narrowing conversion on base type may change value, thus necessary
174
leftIsCast = false;
175             } else {
176                 TypeBinding alternateLeftType = ((CastExpression)left).expression.resolvedType;
177                 if (alternateLeftType == null) return; // cannot do better
178
if ((alternateLeftTypeId = alternateLeftType.id) == leftTypeId) { // obvious identity cast
179
scope.problemReporter().unnecessaryCast((CastExpression)left);
180                     leftIsCast = false;
181                 } else if (alternateLeftTypeId == T_null) {
182                     alternateLeftTypeId = leftTypeId; // tolerate null argument cast
183
leftIsCast = false;
184                 }
185             }
186         }
187         // check need for right operand cast
188
int alternateRightTypeId = rightTypeId;
189         if (rightIsCast) {
190             if ((right.bits & UnnecessaryCast) == 0 && right.resolvedType.isBaseType()) {
191                 // narrowing conversion on base type may change value, thus necessary
192
rightIsCast = false;
193             } else {
194                 TypeBinding alternateRightType = ((CastExpression)right).expression.resolvedType;
195                 if (alternateRightType == null) return; // cannot do better
196
if ((alternateRightTypeId = alternateRightType.id) == rightTypeId) { // obvious identity cast
197
scope.problemReporter().unnecessaryCast((CastExpression)right);
198                     rightIsCast = false;
199                 } else if (alternateRightTypeId == T_null) {
200                     alternateRightTypeId = rightTypeId; // tolerate null argument cast
201
rightIsCast = false;
202                 }
203             }
204         }
205         if (leftIsCast || rightIsCast) {
206             if (alternateLeftTypeId > 15 || alternateRightTypeId > 15) { // must convert String + Object || Object + String
207
if (alternateLeftTypeId == T_JavaLangString) {
208                     alternateRightTypeId = T_JavaLangObject;
209                 } else if (alternateRightTypeId == T_JavaLangString) {
210                     alternateLeftTypeId = T_JavaLangObject;
211                 } else {
212                     return; // invalid operator
213
}
214             }
215             int alternateOperatorSignature = OperatorExpression.OperatorSignatures[operator][(alternateLeftTypeId << 4) + alternateRightTypeId];
216             // (cast) left Op (cast) right --> result
217
// 1111 0000 1111 0000 1111
218
// <<16 <<12 <<8 <<4 <<0
219
final int CompareMASK = (0xF<<16) + (0xF<<8) + 0xF; // mask hiding compile-time types
220
if ((operatorSignature & CompareMASK) == (alternateOperatorSignature & CompareMASK)) { // same promotions and result
221
if (leftIsCast) scope.problemReporter().unnecessaryCast((CastExpression)left);
222                 if (rightIsCast) scope.problemReporter().unnecessaryCast((CastExpression)right);
223             }
224         }
225     }
226
227     private static void checkAlternateBinding(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) {
228
229             InvocationSite fakeInvocationSite = new InvocationSite(){
230                 public TypeBinding[] genericTypeArguments() { return null; }
231                 public boolean isSuperAccess(){ return invocationSite.isSuperAccess(); }
232                 public boolean isTypeAccess() { return invocationSite.isTypeAccess(); }
233                 public void setActualReceiverType(ReferenceBinding actualReceiverType) { /* ignore */}
234                 public void setDepth(int depth) { /* ignore */}
235                 public void setFieldIndex(int depth){ /* ignore */}
236                 public int sourceStart() { return 0; }
237                 public int sourceEnd() { return 0; }
238             };
239             MethodBinding bindingIfNoCast;
240             if (binding.isConstructor()) {
241                 bindingIfNoCast = scope.getConstructor((ReferenceBinding)receiverType, alternateArgumentTypes, fakeInvocationSite);
242             } else {
243                 bindingIfNoCast = receiver.isImplicitThis()
244                     ? scope.getImplicitMethod(binding.selector, alternateArgumentTypes, fakeInvocationSite)
245                     : scope.getMethod(receiverType, binding.selector, alternateArgumentTypes, fakeInvocationSite);
246             }
247             if (bindingIfNoCast == binding) {
248                 int argumentLength = originalArgumentTypes.length;
249                 if (binding.isVarargs()) {
250                     int paramLength = binding.parameters.length;
251                     if (paramLength == argumentLength) {
252                         int varargsIndex = paramLength - 1;
253                         ArrayBinding varargsType = (ArrayBinding) binding.parameters[varargsIndex];
254                         TypeBinding lastArgType = alternateArgumentTypes[varargsIndex];
255                         // originalType may be compatible already, but cast mandated
256
// to clarify between varargs/non-varargs call
257
if (varargsType.dimensions != lastArgType.dimensions()) {
258                             return;
259                         }
260                         if (lastArgType.isCompatibleWith(varargsType.elementsType())
261                                 && lastArgType.isCompatibleWith(varargsType)) {
262                             return;
263                         }
264                     }
265                 }
266                 for (int i = 0; i < argumentLength; i++) {
267                     if (originalArgumentTypes[i] != alternateArgumentTypes[i]) {
268                         scope.problemReporter().unnecessaryCast((CastExpression)arguments[i]);
269                     }
270                 }
271             }
272     }
273     
274     public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) {
275         if (match == castType) {
276             if (!isNarrowing && match == this.resolvedType.leafComponentType()) { // do not tag as unnecessary when recursing through upper bounds
277
tagAsUnnecessaryCast(scope, castType);
278             }
279             return true;
280         }
281         if (match != null && match.isProvablyDistinctFrom(isNarrowing ? expressionType : castType, 0)) {
282             return false;
283         }
284         switch (castType.kind()) {
285             case Binding.PARAMETERIZED_TYPE :
286                 if (castType.isBoundParameterizedType()) {
287                     if (match == null) { // unrelated types
288
this.bits |= UnsafeCast;
289                         return true;
290                     }
291                     switch (match.kind()) {
292                         case Binding.PARAMETERIZED_TYPE :
293                             if (isNarrowing) {
294                                 // [JLS 5.5] T <: S
295
if (expressionType.isRawType() || !expressionType.isEquivalentTo(match)) {
296                                     this.bits |= UnsafeCast;
297                                     return true;
298                                 }
299                                 // [JLS 5.5] S has no subtype X != T, such that |X| == |T|
300
TypeBinding genericCastType = castType.erasure(); // jump to generic type
301
TypeBinding genericMatch = genericCastType.findSuperTypeWithSameErasure(expressionType);
302                                 if (genericMatch == match) {
303                                     this.bits |= UnsafeCast;
304                                 }
305                                 return true;
306                             } else {
307                                 // [JLS 5.5] T >: S
308
if (!match.isEquivalentTo(castType)) {
309                                     this.bits |= UnsafeCast;
310                                     return true;
311                                 }
312                             }
313                             break;
314                         case Binding.RAW_TYPE :
315                             this.bits |= UnsafeCast; // upcast since castType is known to be bound paramType
316
return true;
317                         default :
318                             if (isNarrowing){
319                                 // match is not parameterized or raw, then any other subtype of match will erase to |T|
320
this.bits |= UnsafeCast;
321                                 return true;
322                             }
323                             break;
324                     }
325                 }
326                 break;
327             case Binding.ARRAY_TYPE :
328                 TypeBinding leafType = castType.leafComponentType();
329                 if (isNarrowing && (leafType.isBoundParameterizedType() || leafType.isTypeVariable())) {
330                     this.bits |= UnsafeCast;
331                     return true;
332                 }
333                 break;
334             case Binding.TYPE_PARAMETER :
335                 this.bits |= UnsafeCast;
336                 return true;
337         }
338         if (!isNarrowing && match == this.resolvedType.leafComponentType()) { // do not tag as unnecessary when recursing through upper bounds
339
tagAsUnnecessaryCast(scope, castType);
340         }
341         return true;
342     }
343     
344     /**
345      * Cast expression code generation
346      *
347      * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
348      * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
349      * @param valueRequired boolean
350      */

351     public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
352     
353         int pc = codeStream.position;
354         boolean needRuntimeCheckcast = (this.bits & GenerateCheckcast) != 0;
355         if (constant != Constant.NotAConstant) {
356             if (valueRequired || needRuntimeCheckcast) { // Added for: 1F1W9IG: IVJCOM:WINNT - Compiler omits casting check
357
codeStream.generateConstant(constant, implicitConversion);
358                 if (needRuntimeCheckcast) {
359                     codeStream.checkcast(this.resolvedType);
360                 }
361                 if (!valueRequired) {
362                     // the resolveType cannot be double or long
363
codeStream.pop();
364                 }
365             }
366             codeStream.recordPositionsFrom(pc, this.sourceStart);
367             return;
368         }
369         expression.generateCode(currentScope, codeStream, valueRequired || needRuntimeCheckcast);
370         if (needRuntimeCheckcast && this.expression.postConversionType(currentScope) != this.resolvedType.erasure()) { // no need to issue a checkcast if already done as genericCast
371
codeStream.checkcast(this.resolvedType);
372         }
373         if (valueRequired) {
374             codeStream.generateImplicitConversion(implicitConversion);
375         } else if (needRuntimeCheckcast) {
376             codeStream.pop();
377         }
378         codeStream.recordPositionsFrom(pc, this.sourceStart);
379     }
380
381     public Expression innermostCastedExpression(){
382         Expression current = this.expression;
383         while (current instanceof CastExpression) {
384             current = ((CastExpression) current).expression;
385         }
386         return current;
387     }
388
389     /**
390      * @see org.eclipse.jdt.internal.compiler.ast.Expression#localVariableBinding()
391      */

392     public LocalVariableBinding localVariableBinding() {
393         return this.expression.localVariableBinding();
394     }
395     
396     public int nullStatus(FlowInfo flowInfo) {
397         return this.expression.nullStatus(flowInfo);
398     }
399     
400     /**
401      * @see org.eclipse.jdt.internal.compiler.ast.Expression#optimizedBooleanConstant()
402      */

403     public Constant optimizedBooleanConstant() {
404         switch(this.resolvedType.id) {
405             case T_boolean :
406             case T_JavaLangBoolean :
407                 return this.expression.optimizedBooleanConstant();
408         }
409         return Constant.NotAConstant;
410     }
411     
412     public StringBuffer JavaDoc printExpression(int indent, StringBuffer JavaDoc output) {
413
414         output.append('(');
415         type.print(0, output).append(") "); //$NON-NLS-1$
416
return expression.printExpression(0, output);
417     }
418
419     public TypeBinding resolveType(BlockScope scope) {
420         // compute a new constant if the cast is effective
421

422         // due to the fact an expression may start with ( and that a cast can also start with (
423
// the field is an expression....it can be a TypeReference OR a NameReference Or
424
// any kind of Expression <-- this last one is invalid.......
425

426         constant = Constant.NotAConstant;
427         implicitConversion = T_undefined;
428
429         if ((type instanceof TypeReference) || (type instanceof NameReference)
430                 && ((type.bits & ASTNode.ParenthesizedMASK) >> ASTNode.ParenthesizedSHIFT) == 0) { // no extra parenthesis around type: ((A))exp
431

432             TypeBinding castType = this.resolvedType = type.resolveType(scope);
433             //expression.setExpectedType(this.resolvedType); // needed in case of generic method invocation
434
TypeBinding expressionType = expression.resolveType(scope);
435             if (castType != null) {
436                 if (expressionType != null) {
437                     boolean isLegal = checkCastTypesCompatibility(scope, castType, expressionType, this.expression);
438                     if (isLegal) {
439                         this.expression.computeConversion(scope, castType, expressionType);
440                         if ((this.bits & UnsafeCast) != 0) { // unsafe cast
441
scope.problemReporter().unsafeCast(this, scope);
442                         } else {
443                             if (castType.isRawType() && scope.compilerOptions().getSeverity(CompilerOptions.RawTypeReference) != ProblemSeverities.Ignore){
444                                 scope.problemReporter().rawTypeReference(this.type, castType);
445                             }
446                             if ((this.bits & (UnnecessaryCast|DisableUnnecessaryCastCheck)) == UnnecessaryCast) { // unnecessary cast
447
if (!isIndirectlyUsed()) // used for generic type inference or boxing ?
448
scope.problemReporter().unnecessaryCast(this);
449                             }
450                         }
451                     } else { // illegal cast
452
scope.problemReporter().typeCastError(this, castType, expressionType);
453                         this.bits |= DisableUnnecessaryCastCheck; // disable further secondary diagnosis
454
}
455                 }
456                 this.resolvedType = castType.capture(scope, this.sourceEnd);
457             }
458             return this.resolvedType;
459         } else { // expression as a cast
460
TypeBinding expressionType = expression.resolveType(scope);
461             if (expressionType == null) return null;
462             scope.problemReporter().invalidTypeReference(type);
463             return null;
464         }
465     }
466     
467     /**
468      * @see org.eclipse.jdt.internal.compiler.ast.Expression#setExpectedType(org.eclipse.jdt.internal.compiler.lookup.TypeBinding)
469      */

470     public void setExpectedType(TypeBinding expectedType) {
471         this.expectedType = expectedType;
472     }
473
474     /**
475      * Determines whether apparent unnecessary cast wasn't actually used to
476      * perform return type inference of generic method invocation or boxing.
477      */

478     private boolean isIndirectlyUsed() {
479         if (this.expression instanceof MessageSend) {
480             MethodBinding method = ((MessageSend)this.expression).binding;
481             if (method instanceof ParameterizedGenericMethodBinding
482                         && ((ParameterizedGenericMethodBinding)method).inferredReturnType) {
483                 if (this.expectedType == null)
484                     return true;
485                 if (this.resolvedType != this.expectedType)
486                     return true;
487             }
488         }
489         if (this.expectedType != null && this.resolvedType.isBaseType() && !this.resolvedType.isCompatibleWith(this.expectedType)) {
490             // boxing: Short s = (short) _byte
491
return true;
492         }
493         return false;
494     }
495
496     /**
497      * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsNeedCheckCast()
498      */

499     public void tagAsNeedCheckCast() {
500         this.bits |= GenerateCheckcast;
501     }
502     
503     /**
504      * @see org.eclipse.jdt.internal.compiler.ast.Expression#tagAsUnnecessaryCast(Scope, TypeBinding)
505      */

506     public void tagAsUnnecessaryCast(Scope scope, TypeBinding castType) {
507         if (this.expression.resolvedType == null) return; // cannot do better if expression is not bound
508
this.bits |= UnnecessaryCast;
509     }
510     
511     public void traverse(
512         ASTVisitor visitor,
513         BlockScope blockScope) {
514
515         if (visitor.visit(this, blockScope)) {
516             type.traverse(visitor, blockScope);
517             expression.traverse(visitor, blockScope);
518         }
519         visitor.endVisit(this, blockScope);
520     }
521 }
522
Popular Tags