Applying pteague's patch (re #685)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / CastExpression.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
14 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
15 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
16 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
17 import net.sourceforge.phpdt.internal.compiler.lookup.ArrayBinding;
18 import net.sourceforge.phpdt.internal.compiler.lookup.BaseTypeBinding;
19 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
20 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
21 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
22 import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
23 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
24
25 public class CastExpression extends Expression {
26
27         public Expression expression;
28
29         public Expression type;
30
31         public boolean needRuntimeCheckcast;
32
33         // expression.implicitConversion holds the cast for baseType casting
34         public CastExpression(Expression e, Expression t) {
35                 expression = e;
36                 type = t;
37
38                 // due to the fact an expression may start with ( and that a cast also
39                 // start with (
40                 // the field is an expression....it can be a TypeReference OR a
41                 // NameReference Or
42                 // an expression <--this last one is invalid.......
43
44                 // :-( .............
45
46                 // if (type instanceof TypeReference )
47                 // flag = IsTypeReference ;
48                 // else
49                 // if (type instanceof NameReference)
50                 // flag = IsNameReference ;
51                 // else
52                 // flag = IsExpression ;
53
54         }
55
56         public FlowInfo analyseCode(BlockScope currentScope,
57                         FlowContext flowContext, FlowInfo flowInfo) {
58
59                 return expression.analyseCode(currentScope, flowContext, flowInfo)
60                                 .unconditionalInits();
61         }
62
63         public final void areTypesCastCompatible(BlockScope scope,
64                         TypeBinding castType, TypeBinding expressionType) {
65
66                 // see specifications 5.5
67                 // handle errors and process constant when needed
68
69                 // if either one of the type is null ==>
70                 // some error has been already reported some where ==>
71                 // we then do not report an obvious-cascade-error.
72
73                 needRuntimeCheckcast = false;
74                 if (castType == null || expressionType == null)
75                         return;
76
77                 // identity conversion cannot be performed upfront, due to side-effects
78                 // like constant propagation
79
80                 if (castType.isBaseType()) {
81                         if (expressionType.isBaseType()) {
82                                 if (expressionType == castType) {
83                                         expression.implicitWidening(castType, expressionType);
84                                         constant = expression.constant; // use the same constant
85                                         return;
86                                 }
87                                 if (expressionType.isCompatibleWith(castType)
88                                                 || BaseTypeBinding.isNarrowing(castType.id,
89                                                                 expressionType.id)) {
90                                         expression.implicitConversion = (castType.id << 4)
91                                                         + expressionType.id;
92                                         if (expression.constant != Constant.NotAConstant)
93                                                 constant = expression.constant
94                                                                 .castTo(expression.implicitConversion);
95                                         return;
96                                 }
97                         }
98                         scope.problemReporter().typeCastError(this, castType,
99                                         expressionType);
100                         return;
101                 }
102
103                 // -----------cast to something which is NOT a base
104                 // type--------------------------
105                 if (expressionType == NullBinding) {
106                         // if (castType.isArrayType()){ // 26903 - need checkcast when
107                         // casting null to array type
108                         // needRuntimeCheckcast = true;
109                         // }
110                         return; // null is compatible with every thing
111                 }
112                 if (expressionType.isBaseType()) {
113                         scope.problemReporter().typeCastError(this, castType,
114                                         expressionType);
115                         return;
116                 }
117
118                 if (expressionType.isArrayType()) {
119                         if (castType == expressionType)
120                                 return; // identity conversion
121
122                         if (castType.isArrayType()) {
123                                 // ------- (castType.isArray) expressionType.isArray -----------
124                                 TypeBinding exprElementType = ((ArrayBinding) expressionType)
125                                                 .elementsType(scope);
126                                 if (exprElementType.isBaseType()) {
127                                         // <---stop the recursion-------
128                                         if (((ArrayBinding) castType).elementsType(scope) == exprElementType)
129                                                 needRuntimeCheckcast = true;
130                                         else
131                                                 scope.problemReporter().typeCastError(this, castType,
132                                                                 expressionType);
133                                         return;
134                                 }
135                                 // recursively on the elements...
136                                 areTypesCastCompatible(scope, ((ArrayBinding) castType)
137                                                 .elementsType(scope), exprElementType);
138                                 return;
139                         } else if (castType.isClass()) {
140                                 // ------(castType.isClass) expressionType.isArray
141                                 // ---------------
142                                 if (scope.isJavaLangObject(castType))
143                                         return;
144                         } else { // ------- (castType.isInterface) expressionType.isArray
145                                                 // -----------
146                                 if (scope.isJavaLangCloneable(castType)
147                                                 || scope.isJavaIoSerializable(castType)) {
148                                         needRuntimeCheckcast = true;
149                                         return;
150                                 }
151                         }
152                         scope.problemReporter().typeCastError(this, castType,
153                                         expressionType);
154                         return;
155                 }
156
157                 if (expressionType.isClass()) {
158                         if (castType.isArrayType()) {
159                                 // ---- (castType.isArray) expressionType.isClass -------
160                                 if (scope.isJavaLangObject(expressionType)) { // potential
161                                                                                                                                 // runtime error
162                                         needRuntimeCheckcast = true;
163                                         return;
164                                 }
165                         } else if (castType.isClass()) { // ----- (castType.isClass)
166                                                                                                 // expressionType.isClass ------
167                                 if (expressionType.isCompatibleWith(castType)) { // no
168                                                                                                                                         // runtime
169                                                                                                                                         // error
170                                         if (castType.id == T_String)
171                                                 constant = expression.constant; // (String) cst is still
172                                                                                                                 // a constant
173                                         return;
174                                 }
175                                 if (castType.isCompatibleWith(expressionType)) {
176                                         // potential runtime error
177                                         needRuntimeCheckcast = true;
178                                         return;
179                                 }
180                         } else { // ----- (castType.isInterface) expressionType.isClass
181                                                 // -------
182                                 if (((ReferenceBinding) expressionType).isFinal()) {
183                                         // no subclass for expressionType, thus compile-time check
184                                         // is valid
185                                         if (expressionType.isCompatibleWith(castType))
186                                                 return;
187                                 } else { // a subclass may implement the interface ==> no
188                                                         // check at compile time
189                                         needRuntimeCheckcast = true;
190                                         return;
191                                 }
192                         }
193                         scope.problemReporter().typeCastError(this, castType,
194                                         expressionType);
195                         return;
196                 }
197
198                 // if (expressionType.isInterface()) { cannot be anything else
199                 if (castType.isArrayType()) {
200                         // ----- (castType.isArray) expressionType.isInterface ------
201                         if (scope.isJavaLangCloneable(expressionType)
202                                         || scope.isJavaIoSerializable(expressionType)) // potential
203                                                                                                                                         // runtime
204                                                                                                                                         // error
205                                 needRuntimeCheckcast = true;
206                         else
207                                 scope.problemReporter().typeCastError(this, castType,
208                                                 expressionType);
209                         return;
210                 } else if (castType.isClass()) { // ----- (castType.isClass)
211                                                                                         // expressionType.isInterface
212                                                                                         // --------
213                         if (scope.isJavaLangObject(castType)) // no runtime error
214                                 return;
215                         if (((ReferenceBinding) castType).isFinal()) {
216                                 // no subclass for castType, thus compile-time check is valid
217                                 if (!castType.isCompatibleWith(expressionType)) {
218                                         // potential runtime error
219                                         scope.problemReporter().typeCastError(this, castType,
220                                                         expressionType);
221                                         return;
222                                 }
223                         }
224                 } else { // ----- (castType.isInterface) expressionType.isInterface
225                                         // -------
226                         if (castType == expressionType)
227                                 return; // identity conversion
228                         if (Scope.compareTypes(castType, expressionType) == NotRelated) {
229                                 MethodBinding[] castTypeMethods = ((ReferenceBinding) castType)
230                                                 .methods();
231                                 MethodBinding[] expressionTypeMethods = ((ReferenceBinding) expressionType)
232                                                 .methods();
233                                 int exprMethodsLength = expressionTypeMethods.length;
234                                 for (int i = 0, castMethodsLength = castTypeMethods.length; i < castMethodsLength; i++)
235                                         for (int j = 0; j < exprMethodsLength; j++) {
236                                                 if ((castTypeMethods[i].returnType != expressionTypeMethods[j].returnType)
237                                                                 && (castTypeMethods[i].selector == expressionTypeMethods[j].selector)
238                                                                 && castTypeMethods[i]
239                                                                                 .areParametersEqual(expressionTypeMethods[j])) {
240                                                         scope.problemReporter().typeCastError(this,
241                                                                         castType, expressionType);
242                                                 }
243                                         }
244                         }
245                 }
246                 needRuntimeCheckcast = true;
247                 return;
248         }
249
250         /**
251          * Cast expression code generation
252          * 
253          * @param currentScope
254          *            net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
255          * @param codeStream
256          *            net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
257          * @param valueRequired
258          *            boolean
259          */
260         // public void generateCode(
261         // BlockScope currentScope,
262         // CodeStream codeStream,
263         // boolean valueRequired) {
264         //
265         // int pc = codeStream.position;
266         // if (constant != NotAConstant) {
267         // if (valueRequired
268         // || needRuntimeCheckcast) { // Added for: 1F1W9IG: IVJCOM:WINNT - Compiler
269         // omits casting check
270         // codeStream.generateConstant(constant, implicitConversion);
271         // if (needRuntimeCheckcast) {
272         // codeStream.checkcast(this.resolvedType);
273         // if (!valueRequired)
274         // codeStream.pop();
275         // }
276         // }
277         // codeStream.recordPositionsFrom(pc, this.sourceStart);
278         // return;
279         // }
280         // expression.generateCode(
281         // currentScope,
282         // codeStream,
283         // valueRequired || needRuntimeCheckcast);
284         // if (needRuntimeCheckcast) {
285         // codeStream.checkcast(this.resolvedType);
286         // if (!valueRequired)
287         // codeStream.pop();
288         // } else {
289         // if (valueRequired)
290         // codeStream.generateImplicitConversion(implicitConversion);
291         // }
292         // codeStream.recordPositionsFrom(pc, this.sourceStart);
293         // }
294         public Expression innermostCastedExpression() {
295                 Expression current = this.expression;
296                 while (current instanceof CastExpression) {
297                         current = ((CastExpression) current).expression;
298                 }
299                 return current;
300         }
301
302         public StringBuffer printExpression(int indent, StringBuffer output) {
303
304                 output.append('(');
305                 type.print(0, output).append(") "); //$NON-NLS-1$
306                 return expression.printExpression(0, output);
307         }
308
309         public TypeBinding resolveType(BlockScope scope) {
310                 // compute a new constant if the cast is effective
311
312                 // due to the fact an expression may start with ( and that a cast can
313                 // also start with (
314                 // the field is an expression....it can be a TypeReference OR a
315                 // NameReference Or
316                 // any kind of Expression <-- this last one is invalid.......
317
318                 constant = Constant.NotAConstant;
319                 implicitConversion = T_undefined;
320                 if ((type instanceof TypeReference) || (type instanceof NameReference)) {
321                         this.resolvedType = type.resolveType(scope);
322                         TypeBinding castedExpressionType = expression.resolveType(scope);
323                         if (this.resolvedType != null && castedExpressionType != null) {
324                                 areTypesCastCompatible(scope, this.resolvedType,
325                                                 castedExpressionType);
326                         }
327                         return this.resolvedType;
328                 } else { // expression as a cast !!!!!!!!
329                         TypeBinding castedExpressionType = expression.resolveType(scope);
330                         if (castedExpressionType == null)
331                                 return null;
332                         scope.problemReporter().invalidTypeReference(type);
333                         return null;
334                 }
335         }
336
337         public String toStringExpression() {
338
339                 return "(" + type.toString(0) + ") " + //$NON-NLS-2$ //$NON-NLS-1$
340                                 expression.toStringExpression();
341         }
342
343         public void traverse(ASTVisitor visitor, BlockScope blockScope) {
344
345                 if (visitor.visit(this, blockScope)) {
346                         type.traverse(visitor, blockScope);
347                         expression.traverse(visitor, blockScope);
348                 }
349                 visitor.endVisit(this, blockScope);
350         }
351 }