+/*******************************************************************************
+ * 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;
-import java.util.ArrayList;
+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;
-/**
- * This is a cast expression.
- * @author Matthieu Casanova
- */
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(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 ...)
- */
- public void getOutsideVariable(final List list) {
- }
-
- /**
- * get the modified variables.
- */
- public void getModifiedVariable(final List list) {
- expression.getModifiedVariable(list);
- }
-
- /**
- * Get the variables used.
- */
- public void getUsedVariable(final List list) {
- expression.getUsedVariable(list);
- }
+ 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);
+ }
}