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