blob: df4149eec566e079e3254cb44150a6da6a4c51c5 [file] [log] [blame]
package org.eclipse.jdt.internal.compiler.ast;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import org.eclipse.jdt.internal.compiler.IAbstractSyntaxTreeVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
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) {
/* EXTRA REFERENCE RECORDING
if (!needsRuntimeCheck && (implicitConversion == NoConversion)) {
// need to manually remember the castType as part of the reference information"
currentScope.currentMethodDeclaration().recordReferenceTo(type.getTypeBinding());
}
*/
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 (scope.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 (scope.areTypesCompatible(expressionTb, castTb)) // no runtime error
return;
if (scope.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 (scope.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 (!scope.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);
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);
}
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(){
/*slow code*/
return "("/*nonNLS*/ + type.toString(0)+ ") "/*nonNLS*/ +
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);
}
}