/******************************************************************************* * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v0.5 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v05.html * * Contributors: * IBM Corporation - initial API and implementation ******************************************************************************/ package net.sourceforge.phpdt.internal.compiler.ast; import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor; import net.sourceforge.phpdt.internal.compiler.codegen.CodeStream; 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; public TypeBinding castTb; //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 castTb, TypeBinding expressionTb) { // see specifications p.68 // 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 (castTb == null || expressionTb == null) return; if (castTb.isBaseType()) { if (expressionTb.isBaseType()) { if (expressionTb == castTb) { constant = expression.constant; //use the same constant return; } if (BlockScope.areTypesCompatible(expressionTb, castTb) || BaseTypeBinding.isNarrowing(castTb.id, expressionTb.id)) { expression.implicitConversion = (castTb.id << 4) + expressionTb.id; if (expression.constant != Constant.NotAConstant) constant = expression.constant.castTo(expression.implicitConversion); return; } } scope.problemReporter().typeCastError(this, castTb, expressionTb); return; } //-----------cast to something which is NOT a base type-------------------------- if (expressionTb == NullBinding) return; //null is compatible with every thing if (expressionTb.isBaseType()) { scope.problemReporter().typeCastError(this, castTb, expressionTb); return; } if (expressionTb.isArrayType()) { if (castTb.isArrayType()) { //------- (castTb.isArray) expressionTb.isArray ----------- TypeBinding expressionEltTb = ((ArrayBinding) expressionTb).elementsType(scope); if (expressionEltTb.isBaseType()) { // <---stop the recursion------- if (((ArrayBinding) castTb).elementsType(scope) == expressionEltTb) needRuntimeCheckcast = true; else scope.problemReporter().typeCastError(this, castTb, expressionTb); return; } // recursively on the elements... areTypesCastCompatible( scope, ((ArrayBinding) castTb).elementsType(scope), expressionEltTb); return; } else if ( castTb.isClass()) { //------(castTb.isClass) expressionTb.isArray --------------- if (scope.isJavaLangObject(castTb)) return; } else { //------- (castTb.isInterface) expressionTb.isArray ----------- if (scope.isJavaLangCloneable(castTb) || scope.isJavaIoSerializable(castTb)) { needRuntimeCheckcast = true; return; } } scope.problemReporter().typeCastError(this, castTb, expressionTb); return; } if (expressionTb.isClass()) { if (castTb.isArrayType()) { // ---- (castTb.isArray) expressionTb.isClass ------- if (scope.isJavaLangObject(expressionTb)) { // potential runtime error needRuntimeCheckcast = true; return; } } else if ( castTb.isClass()) { // ----- (castTb.isClass) expressionTb.isClass ------ if (BlockScope.areTypesCompatible(expressionTb, castTb)) // no runtime error return; if (BlockScope.areTypesCompatible(castTb, expressionTb)) { // potential runtime error needRuntimeCheckcast = true; return; } } else { // ----- (castTb.isInterface) expressionTb.isClass ------- if (((ReferenceBinding) expressionTb).isFinal()) { // no subclass for expressionTb, thus compile-time check is valid if (BlockScope.areTypesCompatible(expressionTb, castTb)) return; } else { // a subclass may implement the interface ==> no check at compile time needRuntimeCheckcast = true; return; } } scope.problemReporter().typeCastError(this, castTb, expressionTb); return; } // if (expressionTb.isInterface()) { cannot be anything else if (castTb.isArrayType()) { // ----- (castTb.isArray) expressionTb.isInterface ------ if (scope.isJavaLangCloneable(expressionTb) || scope.isJavaIoSerializable(expressionTb)) // potential runtime error needRuntimeCheckcast = true; else scope.problemReporter().typeCastError(this, castTb, expressionTb); return; } else if ( castTb.isClass()) { // ----- (castTb.isClass) expressionTb.isInterface -------- if (scope.isJavaLangObject(castTb)) // no runtime error return; if (((ReferenceBinding) castTb).isFinal()) { // no subclass for castTb, thus compile-time check is valid if (!BlockScope.areTypesCompatible(castTb, expressionTb)) { // potential runtime error scope.problemReporter().typeCastError(this, castTb, expressionTb); return; } } } else { // ----- (castTb.isInterface) expressionTb.isInterface ------- if (castTb != expressionTb && (Scope.compareTypes(castTb, expressionTb) == NotRelated)) { MethodBinding[] castTbMethods = ((ReferenceBinding) castTb).methods(); MethodBinding[] expressionTbMethods = ((ReferenceBinding) expressionTb).methods(); int exprMethodsLength = expressionTbMethods.length; for (int i = 0, castMethodsLength = castTbMethods.length; i < castMethodsLength; i++) for (int j = 0; j < exprMethodsLength; j++) if (castTbMethods[i].returnType != expressionTbMethods[j].returnType) if (castTbMethods[i].selector == expressionTbMethods[j].selector) if (castTbMethods[i].areParametersEqual(expressionTbMethods[j])) scope.problemReporter().typeCastError(this, castTb, expressionTb); } } needRuntimeCheckcast = true; return; } /** * Cast expression code generation * * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope * @param codeStream org.eclipse.jdt.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(castTb); if (!valueRequired) codeStream.pop(); } } codeStream.recordPositionsFrom(pc, this.sourceStart); return; } expression.generateCode( currentScope, codeStream, valueRequired || needRuntimeCheckcast); if (needRuntimeCheckcast) { codeStream.checkcast(castTb); if (!valueRequired) codeStream.pop(); } else { if (valueRequired) codeStream.generateImplicitConversion(implicitConversion); } codeStream.recordPositionsFrom(pc, this.sourceStart); } 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; TypeBinding expressionTb = expression.resolveType(scope); if (expressionTb == null) return null; if ((type instanceof TypeReference) || (type instanceof NameReference)) { if ((castTb = type.resolveType(scope)) == null) return null; areTypesCastCompatible(scope, castTb, expressionTb); return castTb; } else { // expression as a cast !!!!!!!! scope.problemReporter().invalidTypeReference(type); return null; } } public String toStringExpression() { return "(" + type.toString(0) + ") " + //$NON-NLS-2$ //$NON-NLS-1$ expression.toStringExpression(); } public void traverse( IAbstractSyntaxTreeVisitor visitor, BlockScope blockScope) { if (visitor.visit(this, blockScope)) { type.traverse(visitor, blockScope); expression.traverse(visitor, blockScope); } visitor.endVisit(this, blockScope); } }