e103cccbb9947b50b01541bfe400bf0f40771d14
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / CastExpression.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.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.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.codegen.CodeStream;
15 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
16 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
17 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
18 import net.sourceforge.phpdt.internal.compiler.lookup.ArrayBinding;
19 import net.sourceforge.phpdt.internal.compiler.lookup.BaseTypeBinding;
20 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
21 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
22 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
23 import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
24 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
25
26 public class CastExpression extends Expression {
27
28         public Expression expression;
29         public Expression type;
30         public boolean needRuntimeCheckcast;
31         public TypeBinding castTb;
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 start with (
39                 //the field is an expression....it can be a TypeReference OR a NameReference Or
40                 //an expression <--this last one is invalid.......
41
42                 // :-( .............
43
44                 //if (type instanceof TypeReference )
45                 //      flag = IsTypeReference ;
46                 //else
47                 //      if (type instanceof NameReference)
48                 //              flag = IsNameReference ;
49                 //      else
50                 //              flag = IsExpression ;
51
52         }
53
54         public FlowInfo analyseCode(
55                 BlockScope currentScope,
56                 FlowContext flowContext,
57                 FlowInfo flowInfo) {
58
59                 return expression
60                         .analyseCode(currentScope, flowContext, flowInfo)
61                         .unconditionalInits();
62         }
63
64         public final void areTypesCastCompatible(
65                 BlockScope scope,
66                 TypeBinding castTb,
67                 TypeBinding expressionTb) {
68
69                 // see specifications p.68
70                 // handle errors and process constant when needed
71
72                 // if either one of the type is null ==>
73                 // some error has been already reported some where ==>
74                 // we then do not report an obvious-cascade-error.
75
76                 needRuntimeCheckcast = false;
77                 if (castTb == null || expressionTb == null)
78                         return;
79                 if (castTb.isBaseType()) {
80                         if (expressionTb.isBaseType()) {
81                                 if (expressionTb == castTb) {
82                                         constant = expression.constant; //use the same constant
83                                         return;
84                                 }
85                                 if (BlockScope.areTypesCompatible(expressionTb, castTb)
86                                         || BaseTypeBinding.isNarrowing(castTb.id, expressionTb.id)) {
87                                         expression.implicitConversion = (castTb.id << 4) + expressionTb.id;
88                                         if (expression.constant != Constant.NotAConstant)
89                                                 constant = expression.constant.castTo(expression.implicitConversion);
90                                         return;
91                                 }
92                         }
93                         scope.problemReporter().typeCastError(this, castTb, expressionTb);
94                         return;
95                 }
96
97                 //-----------cast to something which is NOT a base type--------------------------       
98                 if (expressionTb == NullBinding)
99                         return; //null is compatible with every thing
100
101                 if (expressionTb.isBaseType()) {
102                         scope.problemReporter().typeCastError(this, castTb, expressionTb);
103                         return;
104                 }
105
106                 if (expressionTb.isArrayType()) {
107                         if (castTb.isArrayType()) {
108                                 //------- (castTb.isArray) expressionTb.isArray -----------
109                                 TypeBinding expressionEltTb = ((ArrayBinding) expressionTb).elementsType(scope);
110                                 if (expressionEltTb.isBaseType()) {
111                                         // <---stop the recursion------- 
112                                         if (((ArrayBinding) castTb).elementsType(scope) == expressionEltTb)
113                                                 needRuntimeCheckcast = true;
114                                         else
115                                                 scope.problemReporter().typeCastError(this, castTb, expressionTb);
116                                         return;
117                                 }
118                                 // recursively on the elements...
119                                 areTypesCastCompatible(
120                                         scope,
121                                         ((ArrayBinding) castTb).elementsType(scope),
122                                         expressionEltTb);
123                                 return;
124                         } else if (
125                                 castTb.isClass()) {
126                                 //------(castTb.isClass) expressionTb.isArray ---------------   
127                                 if (scope.isJavaLangObject(castTb))
128                                         return;
129                         } else { //------- (castTb.isInterface) expressionTb.isArray -----------
130                                 if (scope.isJavaLangCloneable(castTb) || scope.isJavaIoSerializable(castTb)) {
131                                         needRuntimeCheckcast = true;
132                                         return;
133                                 }
134                         }
135                         scope.problemReporter().typeCastError(this, castTb, expressionTb);
136                         return;
137                 }
138
139                 if (expressionTb.isClass()) {
140                         if (castTb.isArrayType()) {
141                                 // ---- (castTb.isArray) expressionTb.isClass -------
142                                 if (scope.isJavaLangObject(expressionTb)) { // potential runtime error
143                                         needRuntimeCheckcast = true;
144                                         return;
145                                 }
146                         } else if (
147                                 castTb.isClass()) { // ----- (castTb.isClass) expressionTb.isClass ------
148                                 if (BlockScope.areTypesCompatible(expressionTb, castTb)) // no runtime error
149                                         return;
150                                 if (BlockScope.areTypesCompatible(castTb, expressionTb)) {
151                                         // potential runtime  error
152                                         needRuntimeCheckcast = true;
153                                         return;
154                                 }
155                         } else { // ----- (castTb.isInterface) expressionTb.isClass -------  
156                                 if (((ReferenceBinding) expressionTb).isFinal()) {
157                                         // no subclass for expressionTb, thus compile-time check is valid
158                                         if (BlockScope.areTypesCompatible(expressionTb, castTb))
159                                                 return;
160                                 } else { // a subclass may implement the interface ==> no check at compile time
161                                         needRuntimeCheckcast = true;
162                                         return;
163                                 }
164                         }
165                         scope.problemReporter().typeCastError(this, castTb, expressionTb);
166                         return;
167                 }
168
169                 //      if (expressionTb.isInterface()) { cannot be anything else
170                 if (castTb.isArrayType()) {
171                         // ----- (castTb.isArray) expressionTb.isInterface ------
172                         if (scope.isJavaLangCloneable(expressionTb)
173                                 || scope.isJavaIoSerializable(expressionTb)) // potential runtime error
174                                 needRuntimeCheckcast = true;
175                         else
176                                 scope.problemReporter().typeCastError(this, castTb, expressionTb);
177                         return;
178                 } else if (
179                         castTb.isClass()) { // ----- (castTb.isClass) expressionTb.isInterface --------
180                         if (scope.isJavaLangObject(castTb)) // no runtime error
181                                 return;
182                         if (((ReferenceBinding) castTb).isFinal()) {
183                                 // no subclass for castTb, thus compile-time check is valid
184                                 if (!BlockScope.areTypesCompatible(castTb, expressionTb)) {
185                                         // potential runtime error
186                                         scope.problemReporter().typeCastError(this, castTb, expressionTb);
187                                         return;
188                                 }
189                         }
190                 } else { // ----- (castTb.isInterface) expressionTb.isInterface -------
191                         if (castTb != expressionTb
192                                 && (Scope.compareTypes(castTb, expressionTb) == NotRelated)) {
193                                 MethodBinding[] castTbMethods = ((ReferenceBinding) castTb).methods();
194                                 MethodBinding[] expressionTbMethods =
195                                         ((ReferenceBinding) expressionTb).methods();
196                                 int exprMethodsLength = expressionTbMethods.length;
197                                 for (int i = 0, castMethodsLength = castTbMethods.length;
198                                         i < castMethodsLength;
199                                         i++)
200                                         for (int j = 0; j < exprMethodsLength; j++)
201                                                 if (castTbMethods[i].returnType != expressionTbMethods[j].returnType)
202                                                         if (castTbMethods[i].selector == expressionTbMethods[j].selector)
203                                                                 if (castTbMethods[i].areParametersEqual(expressionTbMethods[j]))
204                                                                         scope.problemReporter().typeCastError(this, castTb, expressionTb);
205                         }
206                 }
207                 needRuntimeCheckcast = true;
208                 return;
209         }
210
211         /**
212          * Cast expression code generation
213          *
214          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
215          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
216          * @param valueRequired boolean
217          */
218         public void generateCode(
219                 BlockScope currentScope,
220                 CodeStream codeStream,
221                 boolean valueRequired) {
222
223                 int pc = codeStream.position;
224                 if (constant != NotAConstant) {
225                         if (valueRequired
226                                 || needRuntimeCheckcast) { // Added for: 1F1W9IG: IVJCOM:WINNT - Compiler omits casting check
227                                 codeStream.generateConstant(constant, implicitConversion);
228                                 if (needRuntimeCheckcast) {
229                                         codeStream.checkcast(castTb);
230                                         if (!valueRequired)
231                                                 codeStream.pop();
232                                 }
233                         }
234                         codeStream.recordPositionsFrom(pc, this.sourceStart);
235                         return;
236                 }
237                 expression.generateCode(
238                         currentScope,
239                         codeStream,
240                         valueRequired || needRuntimeCheckcast);
241                 if (needRuntimeCheckcast) {
242                         codeStream.checkcast(castTb);
243                         if (!valueRequired)
244                                 codeStream.pop();
245                 } else {
246                         if (valueRequired)
247                                 codeStream.generateImplicitConversion(implicitConversion);
248                 }
249                 codeStream.recordPositionsFrom(pc, this.sourceStart);
250         }
251
252         public TypeBinding resolveType(BlockScope scope) {
253                 // compute a new constant if the cast is effective
254
255                 // due to the fact an expression may start with ( and that a cast can also start with (
256                 // the field is an expression....it can be a TypeReference OR a NameReference Or
257                 // any kind of Expression <-- this last one is invalid.......
258
259                 constant = Constant.NotAConstant;
260                 implicitConversion = T_undefined;
261                 TypeBinding expressionTb = expression.resolveType(scope);
262                 if (expressionTb == null)
263                         return null;
264
265                 if ((type instanceof TypeReference) || (type instanceof NameReference)) {
266                         if ((castTb = type.resolveType(scope)) == null)
267                                 return null;
268                         areTypesCastCompatible(scope, castTb, expressionTb);
269                         return castTb;
270                 } else { // expression as a cast !!!!!!!! 
271                         scope.problemReporter().invalidTypeReference(type);
272                         return null;
273                 }
274         }
275
276         public String toStringExpression() {
277
278                 return "(" + type.toString(0) + ") " + //$NON-NLS-2$ //$NON-NLS-1$
279                 expression.toStringExpression();
280         }
281
282         public void traverse(
283                 IAbstractSyntaxTreeVisitor visitor,
284                 BlockScope blockScope) {
285
286                 if (visitor.visit(this, blockScope)) {
287                         type.traverse(visitor, blockScope);
288                         expression.traverse(visitor, blockScope);
289                 }
290                 visitor.endVisit(this, blockScope);
291         }
292 }