Refactored packagename to net.sourceforge.phpdt.internal.compiler.ast
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / CastExpression.java
index 9c7da62..950716d 100644 (file)
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
 package net.sourceforge.phpdt.internal.compiler.ast;
 
-/**
- * This is a cast expression.
- * @author Matthieu Casanova
- */
+import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
+import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
+import net.sourceforge.phpdt.internal.compiler.impl.Constant;
+import net.sourceforge.phpdt.internal.compiler.lookup.ArrayBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.BaseTypeBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
+import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
+import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
+
 public class CastExpression extends Expression {
 
-  /** The type in which we cast the expression. */
-  public ConstantIdentifier type;
-
-  /** The expression to be casted. */
-  public Expression expression;
-
-  /**
-   * Create a cast expression.
-   * @param type the type
-   * @param expression the expression
-   * @param sourceStart starting offset
-   * @param sourceEnd ending offset
-   */
-  public CastExpression(ConstantIdentifier type,
-                        Expression expression,
-                        int sourceStart,
-                        int sourceEnd) {
-    super(sourceStart, sourceEnd);
-    this.type = type;
-    this.expression = expression;
-  }
-
-  /**
-   * Return the expression as String.
-   * @return the expression
-   */
-  public String toStringExpression() {
-    final StringBuffer buff = new StringBuffer("(");
-    buff.append(type.toStringExpression());
-    buff.append(") ");
-    buff.append(expression.toStringExpression());
-    return buff.toString();
-  }
+       public Expression expression;
+       public Expression type;
+       public boolean needRuntimeCheckcast;
+
+       //expression.implicitConversion holds the cast for baseType casting 
+       public CastExpression(Expression e, Expression t) {
+               expression = e;
+               type = t;
+
+               //due to the fact an expression may start with ( and that a cast also start with (
+               //the field is an expression....it can be a TypeReference OR a NameReference Or
+               //an expression <--this last one is invalid.......
+
+               // :-( .............
+
+               //if (type instanceof TypeReference )
+               //      flag = IsTypeReference ;
+               //else
+               //      if (type instanceof NameReference)
+               //              flag = IsNameReference ;
+               //      else
+               //              flag = IsExpression ;
+
+       }
+
+       public FlowInfo analyseCode(
+               BlockScope currentScope,
+               FlowContext flowContext,
+               FlowInfo flowInfo) {
+
+               return expression
+                       .analyseCode(currentScope, flowContext, flowInfo)
+                       .unconditionalInits();
+       }
+
+       public final void areTypesCastCompatible(
+               BlockScope scope,
+               TypeBinding castType,
+               TypeBinding expressionType) {
+
+               // see specifications 5.5
+               // handle errors and process constant when needed
+
+               // if either one of the type is null ==>
+               // some error has been already reported some where ==>
+               // we then do not report an obvious-cascade-error.
+
+               needRuntimeCheckcast = false;
+               if (castType == null || expressionType == null) return;
+
+               // identity conversion cannot be performed upfront, due to side-effects
+               // like constant propagation
+                               
+               if (castType.isBaseType()) {
+                       if (expressionType.isBaseType()) {
+                               if (expressionType == castType) {
+                                       expression.implicitWidening(castType, expressionType);
+                                       constant = expression.constant; //use the same constant
+                                       return;
+                               }
+                               if (expressionType.isCompatibleWith(castType)
+                                       || BaseTypeBinding.isNarrowing(castType.id, expressionType.id)) {
+                                       expression.implicitConversion = (castType.id << 4) + expressionType.id;
+                                       if (expression.constant != Constant.NotAConstant)
+                                               constant = expression.constant.castTo(expression.implicitConversion);
+                                       return;
+                               }
+                       }
+                       scope.problemReporter().typeCastError(this, castType, expressionType);
+                       return;
+               }
+
+               //-----------cast to something which is NOT a base type--------------------------       
+               if (expressionType == NullBinding) {
+                       //      if (castType.isArrayType()){ // 26903 - need checkcast when casting null to array type
+                       //              needRuntimeCheckcast = true;
+                       //      }
+                       return; //null is compatible with every thing
+               }
+               if (expressionType.isBaseType()) {
+                       scope.problemReporter().typeCastError(this, castType, expressionType);
+                       return;
+               }
+
+               if (expressionType.isArrayType()) {
+                       if (castType == expressionType) return; // identity conversion
+
+                       if (castType.isArrayType()) {
+                               //------- (castType.isArray) expressionType.isArray -----------
+                               TypeBinding exprElementType = ((ArrayBinding) expressionType).elementsType(scope);
+                               if (exprElementType.isBaseType()) {
+                                       // <---stop the recursion------- 
+                                       if (((ArrayBinding) castType).elementsType(scope) == exprElementType)
+                                               needRuntimeCheckcast = true;
+                                       else
+                                               scope.problemReporter().typeCastError(this, castType, expressionType);
+                                       return;
+                               }
+                               // recursively on the elements...
+                               areTypesCastCompatible(
+                                       scope,
+                                       ((ArrayBinding) castType).elementsType(scope),
+                                       exprElementType);
+                               return;
+                       } else if (
+                               castType.isClass()) {
+                               //------(castType.isClass) expressionType.isArray ---------------       
+                               if (scope.isJavaLangObject(castType))
+                                       return;
+                       } else { //------- (castType.isInterface) expressionType.isArray -----------
+                               if (scope.isJavaLangCloneable(castType) || scope.isJavaIoSerializable(castType)) {
+                                       needRuntimeCheckcast = true;
+                                       return;
+                               }
+                       }
+                       scope.problemReporter().typeCastError(this, castType, expressionType);
+                       return;
+               }
+
+               if (expressionType.isClass()) {
+                       if (castType.isArrayType()) {
+                               // ---- (castType.isArray) expressionType.isClass -------
+                               if (scope.isJavaLangObject(expressionType)) { // potential runtime error
+                                       needRuntimeCheckcast = true;
+                                       return;
+                               }
+                       } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isClass ------
+                               if (expressionType.isCompatibleWith(castType)){ // no runtime error
+                                       if (castType.id == T_String) constant = expression.constant; // (String) cst is still a constant
+                                       return;
+                               }
+                               if (castType.isCompatibleWith(expressionType)) {
+                                       // potential runtime  error
+                                       needRuntimeCheckcast = true;
+                                       return;
+                               }
+                       } else { // ----- (castType.isInterface) expressionType.isClass -------  
+                               if (((ReferenceBinding) expressionType).isFinal()) {
+                                       // no subclass for expressionType, thus compile-time check is valid
+                                       if (expressionType.isCompatibleWith(castType)) 
+                                               return;
+                               } else { // a subclass may implement the interface ==> no check at compile time
+                                       needRuntimeCheckcast = true;
+                                       return;
+                               }
+                       }
+                       scope.problemReporter().typeCastError(this, castType, expressionType);
+                       return;
+               }
+
+               //      if (expressionType.isInterface()) { cannot be anything else
+               if (castType.isArrayType()) {
+                       // ----- (castType.isArray) expressionType.isInterface ------
+                       if (scope.isJavaLangCloneable(expressionType)
+                               || scope.isJavaIoSerializable(expressionType)) // potential runtime error
+                               needRuntimeCheckcast = true;
+                       else
+                               scope.problemReporter().typeCastError(this, castType, expressionType);
+                       return;
+               } else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isInterface --------
+                       if (scope.isJavaLangObject(castType)) // no runtime error
+                               return;
+                       if (((ReferenceBinding) castType).isFinal()) {
+                               // no subclass for castType, thus compile-time check is valid
+                               if (!castType.isCompatibleWith(expressionType)) {
+                                       // potential runtime error
+                                       scope.problemReporter().typeCastError(this, castType, expressionType);
+                                       return;
+                               }
+                       }
+               } else { // ----- (castType.isInterface) expressionType.isInterface -------
+                       if (castType == expressionType) return; // identity conversion
+                       if (Scope.compareTypes(castType, expressionType) == NotRelated) {
+                               MethodBinding[] castTypeMethods = ((ReferenceBinding) castType).methods();
+                               MethodBinding[] expressionTypeMethods =
+                                       ((ReferenceBinding) expressionType).methods();
+                               int exprMethodsLength = expressionTypeMethods.length;
+                               for (int i = 0, castMethodsLength = castTypeMethods.length; i < castMethodsLength; i++)
+                                       for (int j = 0; j < exprMethodsLength; j++) {
+                                               if ((castTypeMethods[i].returnType != expressionTypeMethods[j].returnType)
+                                                               && (castTypeMethods[i].selector == expressionTypeMethods[j].selector)
+                                                               && castTypeMethods[i].areParametersEqual(expressionTypeMethods[j])) {
+                                                       scope.problemReporter().typeCastError(this, castType, expressionType);
+                                               }
+                                       }
+                       }
+               }
+               needRuntimeCheckcast = true;
+               return;
+       }
+
+       /**
+        * Cast expression code generation
+        *
+        * @param currentScope net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
+        * @param codeStream net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
+        * @param valueRequired boolean
+        */
+//     public void generateCode(
+//             BlockScope currentScope,
+//             CodeStream codeStream,
+//             boolean valueRequired) {
+//
+//             int pc = codeStream.position;
+//             if (constant != NotAConstant) {
+//                     if (valueRequired
+//                             || needRuntimeCheckcast) { // Added for: 1F1W9IG: IVJCOM:WINNT - Compiler omits casting check
+//                             codeStream.generateConstant(constant, implicitConversion);
+//                             if (needRuntimeCheckcast) {
+//                                     codeStream.checkcast(this.resolvedType);
+//                                     if (!valueRequired)
+//                                             codeStream.pop();
+//                             }
+//                     }
+//                     codeStream.recordPositionsFrom(pc, this.sourceStart);
+//                     return;
+//             }
+//             expression.generateCode(
+//                     currentScope,
+//                     codeStream,
+//                     valueRequired || needRuntimeCheckcast);
+//             if (needRuntimeCheckcast) {
+//                     codeStream.checkcast(this.resolvedType);
+//                     if (!valueRequired)
+//                             codeStream.pop();
+//             } else {
+//                     if (valueRequired)
+//                             codeStream.generateImplicitConversion(implicitConversion);
+//             }
+//             codeStream.recordPositionsFrom(pc, this.sourceStart);
+//     }
+
+       public Expression innermostCastedExpression(){ 
+               Expression current = this.expression;
+               while (current instanceof CastExpression) {
+                       current = ((CastExpression) current).expression;
+               }
+               return current;
+       }
+       public StringBuffer printExpression(int indent, StringBuffer output) {
+
+               output.append('(');
+               type.print(0, output).append(") "); //$NON-NLS-1$
+               return expression.printExpression(0, output);
+       }
+
+       public TypeBinding resolveType(BlockScope scope) {
+               // compute a new constant if the cast is effective
+
+               // due to the fact an expression may start with ( and that a cast can also start with (
+               // the field is an expression....it can be a TypeReference OR a NameReference Or
+               // any kind of Expression <-- this last one is invalid.......
+
+               constant = Constant.NotAConstant;
+               implicitConversion = T_undefined;
+               if ((type instanceof TypeReference) || (type instanceof NameReference)) {
+                       this.resolvedType = type.resolveType(scope);
+                       TypeBinding castedExpressionType = expression.resolveType(scope);
+                       if (this.resolvedType != null && castedExpressionType != null) {
+                               areTypesCastCompatible(scope, this.resolvedType, castedExpressionType);
+                       }
+                       return this.resolvedType;
+               } else { // expression as a cast !!!!!!!!
+                       TypeBinding castedExpressionType = expression.resolveType(scope);
+                       if (castedExpressionType == null) return null;
+                       scope.problemReporter().invalidTypeReference(type);
+                       return null;
+               }
+       }
+
+       public String toStringExpression() {
+
+               return "(" + type.toString(0) + ") " + //$NON-NLS-2$ //$NON-NLS-1$
+               expression.toStringExpression();
+       }
+
+       public void traverse(
+           ASTVisitor visitor,
+               BlockScope blockScope) {
+
+               if (visitor.visit(this, blockScope)) {
+                       type.traverse(visitor, blockScope);
+                       expression.traverse(visitor, blockScope);
+               }
+               visitor.endVisit(this, blockScope);
+       }
 }