--- /dev/null
+/*******************************************************************************
+ * 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(scope);
+ if (exprElementType.isBaseType()) {
+ // <---stop the recursion-------
+ if (((ArrayBinding) castType).elementsType(scope) != exprElementType)
+ scope.problemReporter().notCompatibleTypesError(this, expressionType, castType);
+ return true;
+ }
+ // recursively on the elements...
+ return checkCastTypesCompatibility(
+ scope,
+ ((ArrayBinding) castType).elementsType(scope),
+ 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);
+ }
+}