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.*; | |
import org.eclipse.jdt.internal.compiler.problem.*; | |
import org.eclipse.jdt.internal.compiler.util.*; | |
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); | |
} | |
} |