/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 292478 - Report potentially null across variable assignment *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast; import org.eclipse.che.ide.ext.java.jdt.core.compiler.CharOperation; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ASTVisitor; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ClassFileConstants; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.codegen.BranchLabel; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.flow.FlowContext; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.impl.Constant; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.ArrayBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.BaseTypeBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.Binding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.ClassScope; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.Scope; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TagBits; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeVariableBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.WildcardBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.problem.ShouldNotImplement; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.util.Messages; import java.util.ArrayList; public abstract class Expression extends Statement { public Constant constant; public int statementEnd = -1; //Some expression may not be used - from a java semantic point //of view only - as statements. Other may. In order to avoid the creation //of wrappers around expression in order to tune them as expression //Expression is a subclass of Statement. See the message isValidJavaStatement() public int implicitConversion; public TypeBinding resolvedType; public static final boolean isConstantValueRepresentable(Constant constant, int constantTypeID, int targetTypeID) { //true if there is no loss of precision while casting. // constantTypeID == constant.typeID if (targetTypeID == constantTypeID) { return true; } switch (targetTypeID) { case T_char: switch (constantTypeID) { case T_char: return true; case T_double: return constant.doubleValue() == constant.charValue(); case T_float: return constant.floatValue() == constant.charValue(); case T_int: return constant.intValue() == constant.charValue(); case T_short: return constant.shortValue() == constant.charValue(); case T_byte: return constant.byteValue() == constant.charValue(); case T_long: return constant.longValue() == constant.charValue(); default: return false;//boolean } case T_float: switch (constantTypeID) { case T_char: return constant.charValue() == constant.floatValue(); case T_double: return constant.doubleValue() == constant.floatValue(); case T_float: return true; case T_int: return constant.intValue() == constant.floatValue(); case T_short: return constant.shortValue() == constant.floatValue(); case T_byte: return constant.byteValue() == constant.floatValue(); case T_long: return constant.longValue() == constant.floatValue(); default: return false;//boolean } case T_double: switch (constantTypeID) { case T_char: return constant.charValue() == constant.doubleValue(); case T_double: return true; case T_float: return constant.floatValue() == constant.doubleValue(); case T_int: return constant.intValue() == constant.doubleValue(); case T_short: return constant.shortValue() == constant.doubleValue(); case T_byte: return constant.byteValue() == constant.doubleValue(); case T_long: return constant.longValue() == constant.doubleValue(); default: return false; //boolean } case T_byte: switch (constantTypeID) { case T_char: return constant.charValue() == constant.byteValue(); case T_double: return constant.doubleValue() == constant.byteValue(); case T_float: return constant.floatValue() == constant.byteValue(); case T_int: return constant.intValue() == constant.byteValue(); case T_short: return constant.shortValue() == constant.byteValue(); case T_byte: return true; case T_long: return constant.longValue() == constant.byteValue(); default: return false; //boolean } case T_short: switch (constantTypeID) { case T_char: return constant.charValue() == constant.shortValue(); case T_double: return constant.doubleValue() == constant.shortValue(); case T_float: return constant.floatValue() == constant.shortValue(); case T_int: return constant.intValue() == constant.shortValue(); case T_short: return true; case T_byte: return constant.byteValue() == constant.shortValue(); case T_long: return constant.longValue() == constant.shortValue(); default: return false; //boolean } case T_int: switch (constantTypeID) { case T_char: return constant.charValue() == constant.intValue(); case T_double: return constant.doubleValue() == constant.intValue(); case T_float: return constant.floatValue() == constant.intValue(); case T_int: return true; case T_short: return constant.shortValue() == constant.intValue(); case T_byte: return constant.byteValue() == constant.intValue(); case T_long: return constant.longValue() == constant.intValue(); default: return false; //boolean } case T_long: switch (constantTypeID) { case T_char: return constant.charValue() == constant.longValue(); case T_double: return constant.doubleValue() == constant.longValue(); case T_float: return constant.floatValue() == constant.longValue(); case T_int: return constant.intValue() == constant.longValue(); case T_short: return constant.shortValue() == constant.longValue(); case T_byte: return constant.byteValue() == constant.longValue(); case T_long: return true; default: return false; //boolean } default: return false; //boolean } } public Expression() { super(); } @Override public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { return flowInfo; } /** * More sophisticated for of the flow analysis used for analyzing expressions, and be able to optimize out * portions of expressions where no actual value is required. * * @param currentScope * @param flowContext * @param flowInfo * @param valueRequired * @return The state of initialization after the analysis of the current expression */ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) { return analyseCode(currentScope, flowContext, flowInfo); } /** Returns false if cast is not legal. */ public final boolean checkCastTypesCompatibility(Scope scope, TypeBinding castType, TypeBinding expressionType, Expression expression) { // see specifications 5.5 // 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. if (castType == null || expressionType == null) { return true; } // identity conversion cannot be performed upfront, due to side-effects // like constant propagation boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; boolean use17specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_7; if (castType.isBaseType()) { if (expressionType.isBaseType()) { if (expressionType == castType) { if (expression != null) { this.constant = expression.constant; //use the same constant } tagAsUnnecessaryCast(scope, castType); return true; } boolean necessary = false; if (expressionType.isCompatibleWith(castType) || (necessary = BaseTypeBinding.isNarrowing(castType.id, expressionType.id))) { if (expression != null) { expression.implicitConversion = (castType.id << 4) + expressionType.id; if (expression.constant != Constant.NotAConstant) { this.constant = expression.constant.castTo(expression.implicitConversion); } } if (!necessary) { tagAsUnnecessaryCast(scope, castType); } return true; } } else if (use17specifics && expressionType.id == TypeIds.T_JavaLangObject) { // cast from Object to base type allowed from 1.7, see JLS $5.5 return true; } else if (use15specifics && scope.environment().computeBoxingType(expressionType) .isCompatibleWith(castType)) { // unboxing - only widening match is allowed tagAsUnnecessaryCast(scope, castType); return true; } return false; } else if (use15specifics && expressionType.isBaseType() && scope.environment().computeBoxingType(expressionType) .isCompatibleWith(castType)) { // boxing - only widening match is allowed tagAsUnnecessaryCast(scope, castType); return true; } switch (expressionType.kind()) { case Binding.BASE_TYPE: //-----------cast to something which is NOT a base type-------------------------- if (expressionType == TypeBinding.NULL) { tagAsUnnecessaryCast(scope, castType); return true; //null is compatible with every thing } return false; case Binding.ARRAY_TYPE: if (castType == expressionType) { tagAsUnnecessaryCast(scope, castType); return true; // identity conversion } switch (castType.kind()) { case Binding.ARRAY_TYPE: // ( ARRAY ) ARRAY TypeBinding castElementType = ((ArrayBinding)castType).elementsType(); TypeBinding exprElementType = ((ArrayBinding)expressionType).elementsType(); if (exprElementType.isBaseType() || castElementType.isBaseType()) { if (castElementType == exprElementType) { tagAsNeedCheckCast(); return true; } return false; } // recurse on array type elements return checkCastTypesCompatibility(scope, castElementType, exprElementType, expression); case Binding.TYPE_PARAMETER: // ( TYPE_PARAMETER ) ARRAY TypeBinding match = expressionType.findSuperTypeOriginatingFrom(castType); if (match == null) { checkUnsafeCast(scope, castType, expressionType, null /*no match*/, true); } // recurse on the type variable upper bound return checkCastTypesCompatibility(scope, ((TypeVariableBinding)castType).upperBound(), expressionType, expression); default: // ( CLASS/INTERFACE ) ARRAY switch (castType.id) { case T_JavaLangCloneable: case T_JavaIoSerializable: tagAsNeedCheckCast(); return true; case T_JavaLangObject: tagAsUnnecessaryCast(scope, castType); return true; default: return false; } } case Binding.TYPE_PARAMETER: TypeBinding match = expressionType.findSuperTypeOriginatingFrom(castType); if (match != null) { return checkUnsafeCast(scope, castType, expressionType, match, false); } // recursively on the type variable upper bound return checkCastTypesCompatibility(scope, castType, ((TypeVariableBinding)expressionType).upperBound(), expression); case Binding.WILDCARD_TYPE: case Binding.INTERSECTION_TYPE: match = expressionType.findSuperTypeOriginatingFrom(castType); if (match != null) { return checkUnsafeCast(scope, castType, expressionType, match, false); } // recursively on the type variable upper bound return checkCastTypesCompatibility(scope, castType, ((WildcardBinding)expressionType).bound, expression); default: if (expressionType.isInterface()) { switch (castType.kind()) { case Binding.ARRAY_TYPE: // ( ARRAY ) INTERFACE switch (expressionType.id) { case T_JavaLangCloneable: case T_JavaIoSerializable: tagAsNeedCheckCast(); return true; default: return false; } case Binding.TYPE_PARAMETER: // ( INTERFACE ) TYPE_PARAMETER match = expressionType.findSuperTypeOriginatingFrom(castType); if (match == null) { checkUnsafeCast(scope, castType, expressionType, null /*no match*/, true); } // recurse on the type variable upper bound return checkCastTypesCompatibility(scope, ((TypeVariableBinding)castType).upperBound(), expressionType, expression); default: if (castType.isInterface()) { // ( INTERFACE ) INTERFACE ReferenceBinding interfaceType = (ReferenceBinding)expressionType; match = interfaceType.findSuperTypeOriginatingFrom(castType); if (match != null) { return checkUnsafeCast(scope, castType, interfaceType, match, false); } tagAsNeedCheckCast(); match = castType.findSuperTypeOriginatingFrom(interfaceType); if (match != null) { return checkUnsafeCast(scope, castType, interfaceType, match, true); } if (use15specifics) { checkUnsafeCast(scope, castType, expressionType, null /*no match*/, true); // ensure there is no collision between both interfaces: i.e. I1 extends List<String>, // I2 extends List<Object> if (scope.compilerOptions().complianceLevel < ClassFileConstants.JDK1_7) { if (interfaceType.hasIncompatibleSuperType((ReferenceBinding)castType)) { return false; } } else if (!castType.isRawType() && interfaceType.hasIncompatibleSuperType((ReferenceBinding)castType)) { return false; } } else { // pre1.5 semantics - no covariance allowed (even if 1.5 compliant, but 1.4 source) // look at original methods rather than the parameterized variants at 1.4 to detect // covariance. Otherwise when confronted with one raw type and one parameterized type, // we could mistakenly detect covariance and scream foul. See https://bugs.eclipse.org/bugs/show_bug // .cgi?id=332744 MethodBinding[] castTypeMethods = getAllOriginalInheritedMethods((ReferenceBinding)castType); MethodBinding[] expressionTypeMethods = getAllOriginalInheritedMethods((ReferenceBinding)expressionType); 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])) { return false; } } } } return true; } else { // ( CLASS ) INTERFACE if (castType.id == TypeIds.T_JavaLangObject) { // no runtime error tagAsUnnecessaryCast(scope, castType); return true; } // can only be a downcast tagAsNeedCheckCast(); match = castType.findSuperTypeOriginatingFrom(expressionType); if (match != null) { return checkUnsafeCast(scope, castType, expressionType, match, true); } if (((ReferenceBinding)castType).isFinal()) { // no subclass for castType, thus compile-time check is invalid return false; } if (use15specifics) { checkUnsafeCast(scope, castType, expressionType, null /*no match*/, true); // ensure there is no collision between both interfaces: i.e. I1 extends List<String>, // I2 extends List<Object> if (scope.compilerOptions().complianceLevel < ClassFileConstants.JDK1_7) { if (((ReferenceBinding)castType) .hasIncompatibleSuperType((ReferenceBinding)expressionType)) { return false; } } else if (!castType.isRawType() && ((ReferenceBinding)castType) .hasIncompatibleSuperType((ReferenceBinding)expressionType)) { return false; } } return true; } } } else { switch (castType.kind()) { case Binding.ARRAY_TYPE: // ( ARRAY ) CLASS if (expressionType.id == TypeIds.T_JavaLangObject) { // potential runtime error if (use15specifics) { checkUnsafeCast(scope, castType, expressionType, expressionType, true); } tagAsNeedCheckCast(); return true; } return false; case Binding.TYPE_PARAMETER: // ( TYPE_PARAMETER ) CLASS match = expressionType.findSuperTypeOriginatingFrom(castType); if (match == null) { checkUnsafeCast(scope, castType, expressionType, null, true); } // recurse on the type variable upper bound return checkCastTypesCompatibility(scope, ((TypeVariableBinding)castType).upperBound(), expressionType, expression); default: if (castType.isInterface()) { // ( INTERFACE ) CLASS ReferenceBinding refExprType = (ReferenceBinding)expressionType; match = refExprType.findSuperTypeOriginatingFrom(castType); if (match != null) { return checkUnsafeCast(scope, castType, expressionType, match, false); } // unless final a subclass may implement the interface ==> no check at compile time if (refExprType.isFinal()) { return false; } tagAsNeedCheckCast(); match = castType.findSuperTypeOriginatingFrom(expressionType); if (match != null) { return checkUnsafeCast(scope, castType, expressionType, match, true); } if (use15specifics) { checkUnsafeCast(scope, castType, expressionType, null /*no match*/, true); // ensure there is no collision between both interfaces: i.e. I1 extends List<String>, // I2 extends List<Object> if (scope.compilerOptions().complianceLevel < ClassFileConstants.JDK1_7) { if (refExprType.hasIncompatibleSuperType((ReferenceBinding)castType)) { return false; } } else if (!castType.isRawType() && refExprType.hasIncompatibleSuperType((ReferenceBinding)castType)) { return false; } } return true; } else { // ( CLASS ) CLASS match = expressionType.findSuperTypeOriginatingFrom(castType); if (match != null) { if (expression != null && castType.id == TypeIds.T_JavaLangString) { this.constant = expression.constant; // (String) cst is still a constant } return checkUnsafeCast(scope, castType, expressionType, match, false); } match = castType.findSuperTypeOriginatingFrom(expressionType); if (match != null) { tagAsNeedCheckCast(); return checkUnsafeCast(scope, castType, expressionType, match, true); } return false; } } } } } /** * Check the local variable of this expression, if any, against potential NPEs * given a flow context and an upstream flow info. If so, report the risk to * the context. Marks the local as checked, which affects the flow info. * * @param scope * the scope of the analysis * @param flowContext * the current flow context * @param flowInfo * the upstream flow info; caveat: may get modified */ public void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) { LocalVariableBinding local = localVariableBinding(); if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { if ((this.bits & ASTNode.IsNonNull) == 0) { flowContext.recordUsingNullReference(scope, local, this, FlowContext.MAY_NULL, flowInfo); } flowInfo.markAsComparedEqualToNonNull(local); // from thereon it is set if ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0) { flowInfo.markedAsNullOrNonNullInAssertExpression(local); } if (flowContext.initsOnFinally != null) { flowContext.initsOnFinally.markAsComparedEqualToNonNull(local); if ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0) { flowContext.initsOnFinally.markedAsNullOrNonNullInAssertExpression(local); } } } } public boolean checkUnsafeCast(Scope scope, TypeBinding castType, TypeBinding expressionType, TypeBinding match, boolean isNarrowing) { if (match == castType) { if (!isNarrowing) { tagAsUnnecessaryCast(scope, castType); } return true; } if (match != null && (!castType.isReifiable() || !expressionType.isReifiable())) { if (isNarrowing ? match.isProvablyDistinct(expressionType) : castType.isProvablyDistinct(match)) { return false; } } if (!isNarrowing) { tagAsUnnecessaryCast(scope, castType); } return true; } /** * Base types need that the widening is explicitly done by the compiler using some bytecode like i2f. * Also check unsafe type operations. */ public void computeConversion(Scope scope, TypeBinding runtimeType, TypeBinding compileTimeType) { if (runtimeType == null || compileTimeType == null) { return; } if (this.implicitConversion != 0) { return; // already set independantly } // it is possible for a Byte to be unboxed to a byte & then converted to an int // but it is not possible for a byte to become Byte & then assigned to an Integer, // or to become an int before boxed into an Integer if (runtimeType != TypeBinding.NULL && runtimeType.isBaseType()) { if (!compileTimeType.isBaseType()) { TypeBinding unboxedType = scope.environment().computeBoxingType(compileTimeType); this.implicitConversion = TypeIds.UNBOXING; scope.problemReporter().autoboxing(this, compileTimeType, runtimeType); compileTimeType = unboxedType; } } else if (compileTimeType != TypeBinding.NULL && compileTimeType.isBaseType()) { TypeBinding boxedType = scope.environment().computeBoxingType(runtimeType); if (boxedType == runtimeType) { boxedType = compileTimeType; } this.implicitConversion = TypeIds.BOXING | (boxedType.id << 4) + compileTimeType.id; scope.problemReporter().autoboxing(this, compileTimeType, scope.environment().computeBoxingType(boxedType)); return; } else if (this.constant != Constant.NotAConstant && this.constant.typeID() != TypeIds.T_JavaLangString) { this.implicitConversion = TypeIds.BOXING; return; } int compileTimeTypeID, runtimeTypeID; if ((compileTimeTypeID = compileTimeType.id) == TypeIds.NoId) { // e.g. ? extends String ==> String (103227) compileTimeTypeID = compileTimeType.erasure().id == TypeIds.T_JavaLangString ? TypeIds.T_JavaLangString : TypeIds.T_JavaLangObject; } switch (runtimeTypeID = runtimeType.id) { case T_byte: case T_short: case T_char: if (compileTimeTypeID == TypeIds.T_JavaLangObject) { this.implicitConversion |= (runtimeTypeID << 4) + compileTimeTypeID; } else { this.implicitConversion |= (TypeIds.T_int << 4) + compileTimeTypeID; } break; case T_JavaLangString: case T_float: case T_boolean: case T_double: case T_int: //implicitConversion may result in i2i which will result in NO code gen case T_long: this.implicitConversion |= (runtimeTypeID << 4) + compileTimeTypeID; break; default: // regular object ref // if (compileTimeType.isRawType() && runtimeTimeType.isBoundParameterizedType()) { // scope.problemReporter().unsafeRawExpression(this, compileTimeType, runtimeTimeType); // } } } /** * Expression statements are plain expressions, however they generate like * normal expressions with no value required. * * @param currentScope * org.eclipse.che.ide.java.client.internal.compiler.lookup.BlockScope * @param codeStream * org.eclipse.che.ide.java.client.internal.compiler.codegen.CodeStream */ @Override public void generateCode(BlockScope currentScope) { if ((this.bits & ASTNode.IsReachable) == 0) { return; } generateCode(currentScope, false); } /** * Every expression is responsible for generating its implicit conversion when necessary. * * @param currentScope * org.eclipse.che.ide.java.client.internal.compiler.lookup.BlockScope * @param codeStream * org.eclipse.che.ide.java.client.internal.compiler.codegen.CodeStream * @param valueRequired * boolean */ public void generateCode(BlockScope currentScope, boolean valueRequired) { if (this.constant != Constant.NotAConstant) { } else { // actual non-constant code generation throw new ShouldNotImplement(Messages.instance.ast_missingCode()); } } /** * Default generation of a boolean value * * @param currentScope * @param codeStream * @param trueLabel * @param falseLabel * @param valueRequired */ public void generateOptimizedBoolean(BlockScope currentScope, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) { // a label valued to nil means: by default we fall through the case... // both nil means we leave the value on the stack Constant cst = optimizedBooleanConstant(); generateCode(currentScope, valueRequired && cst == Constant.NotAConstant); } /* Optimized (java) code generation for string concatenations that involve StringBuffer * creation: going through this path means that there is no need for a new StringBuffer * creation, further operands should rather be only appended to the current one. * By default: no optimization. */ public void generateOptimizedStringConcatenation(BlockScope blockScope, int typeID) { if (typeID == TypeIds.T_JavaLangString && this.constant != Constant.NotAConstant && this.constant.stringValue().length() == 0) { return; // optimize str + "" } generateCode(blockScope, true); } private MethodBinding[] getAllOriginalInheritedMethods(ReferenceBinding binding) { ArrayList collector = new ArrayList(); getAllInheritedMethods0(binding, collector); for (int i = 0, len = collector.size(); i < len; i++) { collector.set(i, ((MethodBinding)collector.get(i)).original()); } return (MethodBinding[])collector.toArray(new MethodBinding[collector.size()]); } private void getAllInheritedMethods0(ReferenceBinding binding, ArrayList collector) { if (!binding.isInterface()) { return; } MethodBinding[] methodBindings = binding.methods(); for (int i = 0, max = methodBindings.length; i < max; i++) { collector.add(methodBindings[i]); } ReferenceBinding[] superInterfaces = binding.superInterfaces(); for (int i = 0, max = superInterfaces.length; i < max; i++) { getAllInheritedMethods0(superInterfaces[i], collector); } } public static Binding getDirectBinding(Expression someExpression) { if ((someExpression.bits & ASTNode.IgnoreNoEffectAssignCheck) != 0) { return null; } if (someExpression instanceof SingleNameReference) { return ((SingleNameReference)someExpression).binding; } else if (someExpression instanceof FieldReference) { FieldReference fieldRef = (FieldReference)someExpression; if (fieldRef.receiver.isThis() && !(fieldRef.receiver instanceof QualifiedThisReference)) { return fieldRef.binding; } } else if (someExpression instanceof Assignment) { Expression lhs = ((Assignment)someExpression).lhs; if ((lhs.bits & ASTNode.IsStrictlyAssigned) != 0) { // i = i = ...; // eq to int i = ...; return getDirectBinding(((Assignment)someExpression).lhs); } else if (someExpression instanceof PrefixExpression) { // i = i++; // eq to ++i; return getDirectBinding(((Assignment)someExpression).lhs); } } else if (someExpression instanceof QualifiedNameReference) { QualifiedNameReference qualifiedNameReference = (QualifiedNameReference)someExpression; if (qualifiedNameReference.indexOfFirstFieldBinding != 1 && qualifiedNameReference.otherBindings == null) { // case where a static field is retrieved using ClassName.fieldname return qualifiedNameReference.binding; } } else if (someExpression.isThis()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=276741 return someExpression.resolvedType; } // } else if (someExpression instanceof PostfixExpression) { // recurse for postfix: i++ --> i // // note: "b = b++" is equivalent to doing nothing, not to "b++" // return getDirectBinding(((PostfixExpression) someExpression).lhs); return null; } public boolean isCompactableOperation() { return false; } //Return true if the conversion is done AUTOMATICALLY by the vm //while the javaVM is an int based-machine, thus for example pushing //a byte onto the stack , will automatically create an int on the stack //(this request some work d be done by the VM on signed numbers) public boolean isConstantValueOfTypeAssignableToType(TypeBinding constantType, TypeBinding targetType) { if (this.constant == Constant.NotAConstant) { return false; } if (constantType == targetType) { return true; } //No free assignment conversion from anything but to integral ones. if (BaseTypeBinding.isWidening(TypeIds.T_int, constantType.id) && (BaseTypeBinding.isNarrowing(targetType.id, TypeIds.T_int))) { //use current explicit conversion in order to get some new value to compare with current one return isConstantValueRepresentable(this.constant, constantType.id, targetType.id); } return false; } public boolean isTypeReference() { return false; } /** * Returns the local variable referenced by this node. Can be a direct reference (SingleNameReference) * or thru a cast expression etc... */ public LocalVariableBinding localVariableBinding() { return null; } /** * Mark this expression as being non null, per a specific tag in the * source code. */ // this is no more called for now, waiting for inter procedural null reference analysis public void markAsNonNull() { this.bits |= ASTNode.IsNonNull; } public int nullStatus(FlowInfo flowInfo) { if (/* (this.bits & IsNonNull) != 0 || */ this.constant != null && this.constant != Constant.NotAConstant) { return FlowInfo.NON_NULL; // constant expression cannot be null } LocalVariableBinding local = localVariableBinding(); if (local != null) { return flowInfo.nullStatus(local); } return FlowInfo.NON_NULL; } /** * Constant usable for bytecode pattern optimizations, but cannot be inlined * since it is not strictly equivalent to the definition of constant expressions. * In particular, some side-effects may be required to occur (only the end value * is known). * * @return Constant known to be of boolean type */ public Constant optimizedBooleanConstant() { return this.constant; } /** * Returns the type of the expression after required implicit conversions. When expression type gets promoted * or inserted a generic cast, the converted type will differ from the resolved type (surface side-effects from * #computeConversion(...)). * * @return the type after implicit conversion */ public TypeBinding postConversionType(Scope scope) { TypeBinding convertedType = this.resolvedType; int runtimeType = (this.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4; switch (runtimeType) { case T_boolean: convertedType = TypeBinding.BOOLEAN; break; case T_byte: convertedType = TypeBinding.BYTE; break; case T_short: convertedType = TypeBinding.SHORT; break; case T_char: convertedType = TypeBinding.CHAR; break; case T_int: convertedType = TypeBinding.INT; break; case T_float: convertedType = TypeBinding.FLOAT; break; case T_long: convertedType = TypeBinding.LONG; break; case T_double: convertedType = TypeBinding.DOUBLE; break; default: } if ((this.implicitConversion & TypeIds.BOXING) != 0) { convertedType = scope.environment().computeBoxingType(convertedType); } return convertedType; } @Override public StringBuffer print(int indent, StringBuffer output) { printIndent(indent, output); return printExpression(indent, output); } public abstract StringBuffer printExpression(int indent, StringBuffer output); @Override public StringBuffer printStatement(int indent, StringBuffer output) { return print(indent, output).append(";"); //$NON-NLS-1$ } @Override public void resolve(BlockScope scope) { // drops the returning expression's type whatever the type is. this.resolveType(scope); return; } /** * Resolve the type of this expression in the context of a blockScope * * @param scope * @return Return the actual type of this expression after resolution */ public TypeBinding resolveType(BlockScope scope) { // by default... subclasses should implement a better TB if required. return null; } /** * Resolve the type of this expression in the context of a classScope * * @param scope * @return Return the actual type of this expression after resolution */ public TypeBinding resolveType(ClassScope scope) { // by default... subclasses should implement a better TB if required. return null; } public TypeBinding resolveTypeExpecting(BlockScope scope, TypeBinding expectedType) { setExpectedType(expectedType); // needed in case of generic method invocation TypeBinding expressionType = this.resolveType(scope); if (expressionType == null) { return null; } if (expressionType == expectedType) { return expressionType; } if (!expressionType.isCompatibleWith(expectedType)) { if (scope.isBoxingCompatibleWith(expressionType, expectedType)) { computeConversion(scope, expectedType, expressionType); } else { scope.problemReporter().typeMismatchError(expressionType, expectedType, this, null); return null; } } return expressionType; } /** * Returns true if the receiver is forced to be of raw type either to satisfy the contract imposed * by a super type or because it *is* raw and the current type has no control over it (i.e the rawness * originates from some other file.) */ public boolean forcedToBeRaw(ReferenceContext referenceContext) { if (this instanceof NameReference) { final Binding receiverBinding = ((NameReference)this).binding; if (receiverBinding.isParameter() && (((LocalVariableBinding)receiverBinding).tagBits & TagBits.ForcedToBeRawType) != 0) { return true; // parameter is forced to be raw since super method uses raw types. } else if (receiverBinding instanceof FieldBinding) { FieldBinding field = (FieldBinding)receiverBinding; if (field.type.isRawType()) { if (referenceContext instanceof AbstractMethodDeclaration) { AbstractMethodDeclaration methodDecl = (AbstractMethodDeclaration)referenceContext; if (field.declaringClass != methodDecl.binding.declaringClass) { // inherited raw field, see https://bugs.eclipse.org/bugs/show_bug // .cgi?id=337962 return true; } } else if (referenceContext instanceof TypeDeclaration) { TypeDeclaration type = (TypeDeclaration)referenceContext; if (field.declaringClass != type.binding) { // inherited raw field, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=337962 return true; } } } } } else if (this instanceof MessageSend) { if (!CharOperation.equals(((MessageSend)this).binding.declaringClass.getFileName(), referenceContext .compilationResult().getFileName())) { // problem is rooted elsewhere return true; } } else if (this instanceof FieldReference) { FieldBinding field = ((FieldReference)this).binding; if (!CharOperation.equals(field.declaringClass.getFileName(), referenceContext.compilationResult() .getFileName())) { // problem is rooted elsewhere return true; } if (field.type.isRawType()) { if (referenceContext instanceof AbstractMethodDeclaration) { AbstractMethodDeclaration methodDecl = (AbstractMethodDeclaration)referenceContext; if (field.declaringClass != methodDecl.binding.declaringClass) { // inherited raw field, see https://bugs.eclipse.org/bugs/show_bug // .cgi?id=337962 return true; } } else if (referenceContext instanceof TypeDeclaration) { TypeDeclaration type = (TypeDeclaration)referenceContext; if (field.declaringClass != type.binding) { // inherited raw field, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=337962 return true; } } } } else if (this instanceof ConditionalExpression) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=337751 ConditionalExpression ternary = (ConditionalExpression)this; if (ternary.valueIfTrue.forcedToBeRaw(referenceContext) || ternary.valueIfFalse.forcedToBeRaw(referenceContext)) { return true; } } return false; } /** * Returns an object which can be used to identify identical JSR sequence targets * (see TryStatement subroutine codegen) * or <code>null</null> if not reusable */ public Object reusableJSRTarget() { if (this.constant != Constant.NotAConstant) { return this.constant; } return null; } /** * Record the type expectation before this expression is typechecked. * e.g. String s = foo();, foo() will be tagged as being expected of type String * Used to trigger proper inference of generic method invocations. * * @param expectedType * The type denoting an expectation in the context of an assignment conversion */ public void setExpectedType(TypeBinding expectedType) { // do nothing by default } public void tagAsNeedCheckCast() { // do nothing by default } /** * Record the fact a cast expression got detected as being unnecessary. * * @param scope * @param castType */ public void tagAsUnnecessaryCast(Scope scope, TypeBinding castType) { // do nothing by default } public Expression toTypeReference() { //by default undefined //this method is meanly used by the parser in order to transform //an expression that is used as a type reference in a cast .... //--appreciate the fact that castExpression and ExpressionWithParenthesis //--starts with the same pattern..... return this; } /** * Traverse an expression in the context of a blockScope * * @param visitor * @param scope */ @Override public void traverse(ASTVisitor visitor, BlockScope scope) { // nothing to do } /** * Traverse an expression in the context of a classScope * * @param visitor * @param scope */ public void traverse(ASTVisitor visitor, ClassScope scope) { // nothing to do } }