+/*******************************************************************************
+ * 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;
-import java.util.List;
-
-/**
- * This is a cast expression.
- * @author Matthieu Casanova
- */
-public final class CastExpression extends Expression {
-
- /** The type in which we cast the expression. */
- private final ConstantIdentifier type;
-
- /** The expression to be casted. */
- private final Expression expression;
-
- /**
- * Create a cast expression.
- * @param type the type
- * @param expression the expression
- * @param sourceStart starting offset
- * @param sourceEnd ending offset
- */
- public CastExpression(final ConstantIdentifier type,
- final Expression expression,
- final int sourceStart,
- final 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();
- }
-
- /**
- * Get the variables from outside (parameters, globals ...)
- *
- * @param list the list where we will put variables
- */
- public void getOutsideVariable(final List list) {}
-
- /**
- * get the modified variables.
- *
- * @param list the list where we will put variables
- */
- public void getModifiedVariable(final List list) {
- expression.getModifiedVariable(list);
- }
-
- /**
- * Get the variables used.
- *
- * @param list the list where we will put variables
- */
- public void getUsedVariable(final List list) {
- expression.getUsedVariable(list);
- }
+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 {
+
+ 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);
+ }
}