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