blob: ecb59282c30d543b4ae197628aeb5c312db95966 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
public class InstanceOfExpression extends OperatorExpression {
public Expression expression;
public TypeReference type;
public InstanceOfExpression(
Expression expression,
TypeReference type,
int operator) {
this.expression = expression;
this.type = type;
this.bits |= operator << OperatorSHIFT;
this.sourceStart = expression.sourceStart;
this.sourceEnd = type.sourceEnd;
}
public FlowInfo analyseCode(
BlockScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo) {
return expression
.analyseCode(currentScope, flowContext, flowInfo)
.unconditionalInits();
}
/**
* Returns false if the instanceof unnecessary
*/
public final boolean checkCastTypesCompatibility(
BlockScope scope,
TypeBinding castType,
TypeBinding expressionType) {
//A more complete version of this method is provided on
//CastExpression (it deals with constant and need runtime checkcast)
if (castType == expressionType) return false;
//by grammatical construction, the base type check is not necessary
if (castType == null || expressionType == null) return true;
//-----------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 false; //null is compatible with every thing
}
if (expressionType.isBaseType()) {
scope.problemReporter().notCompatibleTypesError(this, expressionType, castType);
return true;
}
if (expressionType.isArrayType()) {
if (castType == expressionType) return false; // identity conversion
if (castType.isArrayType()) {
//------- (castType.isArray) expressionType.isArray -----------
TypeBinding exprElementType = ((ArrayBinding) expressionType).elementsType();
if (exprElementType.isBaseType()) {
// <---stop the recursion-------
if (((ArrayBinding) castType).elementsType() != exprElementType)
scope.problemReporter().notCompatibleTypesError(this, expressionType, castType);
return true;
}
// recursively on the elements...
return checkCastTypesCompatibility(
scope,
((ArrayBinding) castType).elementsType(),
exprElementType);
} else if (
castType.isClass()) {
//------(castType.isClass) expressionType.isArray ---------------
if (castType.id == T_Object) {
return false;
}
} else { //------- (castType.isInterface) expressionType.isArray -----------
if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) {
return true;
}
}
scope.problemReporter().notCompatibleTypesError(this, expressionType, castType);
return true;
}
if (expressionType.isClass()) {
if (castType.isArrayType()) {
// ---- (castType.isArray) expressionType.isClass -------
if (expressionType.id == T_Object) { // potential runtime error
return true;
}
} else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isClass ------
if (expressionType.isCompatibleWith(castType)){ // no runtime error
return false;
}
if (castType.isCompatibleWith(expressionType)) {
// potential runtime error
return true;
}
} else { // ----- (castType.isInterface) expressionType.isClass -------
if (expressionType.isCompatibleWith(castType))
return false;
if (!((ReferenceBinding) expressionType).isFinal()) {
// a subclass may implement the interface ==> no check at compile time
return true;
}
// no subclass for expressionType, thus compile-time check is valid
}
scope.problemReporter().notCompatibleTypesError(this, expressionType, castType);
return true;
}
// if (expressionType.isInterface()) { cannot be anything else
if (castType.isArrayType()) {
// ----- (castType.isArray) expressionType.isInterface ------
if (!(expressionType.id == T_JavaLangCloneable
|| expressionType.id == T_JavaIoSerializable)) {// potential runtime error
scope.problemReporter().notCompatibleTypesError(this, expressionType, castType);
}
return true;
} else if (castType.isClass()) { // ----- (castType.isClass) expressionType.isInterface --------
if (castType.id == T_Object) { // no runtime error
return false;
}
if (((ReferenceBinding) castType).isFinal()) {
// no subclass for castType, thus compile-time check is valid
if (!castType.isCompatibleWith(expressionType)) {
// potential runtime error
scope.problemReporter().notCompatibleTypesError(this, expressionType, castType);
return true;
}
}
} else { // ----- (castType.isInterface) expressionType.isInterface -------
if (expressionType.isCompatibleWith(castType)) {
return false;
}
if (!castType.isCompatibleWith(expressionType)) {
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)
&& CharOperation.equals(castTypeMethods[i].selector, expressionTypeMethods[j].selector)
&& castTypeMethods[i].areParametersEqual(expressionTypeMethods[j])) {
scope.problemReporter().notCompatibleTypesError(this, expressionType, castType);
}
}
}
}
return true;
}
/**
* Code generation for instanceOfExpression
*
* @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;
expression.generateCode(currentScope, codeStream, true);
codeStream.instance_of(type.resolvedType);
if (!valueRequired)
codeStream.pop();
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
expression.printExpression(indent, output).append(" instanceof "); //$NON-NLS-1$
return type.print(0, output);
}
public TypeBinding resolveType(BlockScope scope) {
constant = NotAConstant;
TypeBinding expressionType = expression.resolveType(scope);
TypeBinding checkType = type.resolveType(scope);
if (expressionType == null || checkType == null)
return null;
boolean necessary = checkCastTypesCompatibility(scope, checkType, expressionType);
if (!necessary) {
scope.problemReporter().unnecessaryInstanceof(this, checkType);
}
return this.resolvedType = BooleanBinding;
}
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
expression.traverse(visitor, scope);
type.traverse(visitor, scope);
}
visitor.endVisit(this, scope);
}
}