/******************************************************************************* * Copyright (c) 2000, 2013 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> - Contributions for * bug 185682 - Increment/decrement operators mark local variables as read * bug 331649 - [compiler][null] consider null annotations for fields * bug 383368 - [compiler][null] syntactic null analysis for field references * Bug 412203 - [compiler] Internal compiler error: java.lang.IllegalArgumentException: info cannot be null * Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec) * Jesper S Moller - Contributions for * Bug 378674 - "The method can be declared as static" is wrong *******************************************************************************/ 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.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; import org.eclipse.jdt.internal.compiler.codegen.Opcodes; import org.eclipse.jdt.internal.compiler.flow.FlowContext; import org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18; import org.eclipse.jdt.internal.compiler.lookup.InvocationSite; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.MissingTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TagBits; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; public class FieldReference extends Reference implements InvocationSite { public static final int READ = 0; public static final int WRITE = 1; public Expression receiver; public char[] token; public FieldBinding binding; // exact binding resulting from lookup public MethodBinding[] syntheticAccessors; // [0]=read accessor [1]=write accessor public long nameSourcePosition; //(start<<32)+end public TypeBinding actualReceiverType; public TypeBinding genericCast; public FieldReference(char[] source, long pos) { this.token = source; this.nameSourcePosition = pos; //by default the position are the one of the field (not true for super access) this.sourceStart = (int) (pos >>> 32); this.sourceEnd = (int) (pos & 0x00000000FFFFFFFFL); this.bits |= Binding.FIELD; } public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, boolean isCompound) { // compound assignment extra work if (isCompound) { // check the variable part is initialized if blank final if (this.binding.isBlankFinal() && this.receiver.isThis() && currentScope.needBlankFinalFieldInitializationCheck(this.binding)) { FlowInfo fieldInits = flowContext.getInitsForFinalBlankInitializationCheck(this.binding.declaringClass.original(), flowInfo); if (!fieldInits.isDefinitelyAssigned(this.binding)) { currentScope.problemReporter().uninitializedBlankFinalField(this.binding, this); // we could improve error msg here telling "cannot use compound assignment on final blank field" } } manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/); } flowInfo = this.receiver .analyseCode(currentScope, flowContext, flowInfo, !this.binding.isStatic()) .unconditionalInits(); if (assignment.expression != null) { flowInfo = assignment .expression .analyseCode(currentScope, flowContext, flowInfo) .unconditionalInits(); } manageSyntheticAccessIfNecessary(currentScope, flowInfo, false /*write-access*/); // check if assigning a final field if (this.binding.isFinal()) { // in a context where it can be assigned? if (this.binding.isBlankFinal() && !isCompound && this.receiver.isThis() && !(this.receiver instanceof QualifiedThisReference) && ((this.receiver.bits & ASTNode.ParenthesizedMASK) == 0) // (this).x is forbidden && currentScope.allowBlankFinalFieldAssignment(this.binding)) { if (flowInfo.isPotentiallyAssigned(this.binding)) { currentScope.problemReporter().duplicateInitializationOfBlankFinalField( this.binding, this); } else { flowContext.recordSettingFinal(this.binding, this, flowInfo); } flowInfo.markAsDefinitelyAssigned(this.binding); } else { // assigning a final field outside an initializer or constructor or wrong reference currentScope.problemReporter().cannotAssignToFinalField(this.binding, this); } } else if (this.binding.isNonNull()) { // in a context where it can be assigned? if ( !isCompound && this.receiver.isThis() && !(this.receiver instanceof QualifiedThisReference) && TypeBinding.equalsEquals(this.receiver.resolvedType, this.binding.declaringClass) // inherited fields are not tracked here && ((this.receiver.bits & ASTNode.ParenthesizedMASK) == 0)) { // (this).x is forbidden flowInfo.markAsDefinitelyAssigned(this.binding); } } return flowInfo; } public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { return analyseCode(currentScope, flowContext, flowInfo, true); } public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) { boolean nonStatic = !this.binding.isStatic(); this.receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic); if (nonStatic) { this.receiver.checkNPE(currentScope, flowContext, flowInfo); } if (valueRequired || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) { manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/); } return flowInfo; } public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) { if (flowContext.isNullcheckedFieldAccess(this)) { return true; // enough seen } return checkNullableFieldDereference(scope, this.binding, this.nameSourcePosition); } /** * @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding) */ public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBinding compileTimeType) { if (runtimeTimeType == null || compileTimeType == null) return; // set the generic cast after the fact, once the type expectation is fully known (no need for strict cast) if (this.binding != null && this.binding.isValidBinding()) { FieldBinding originalBinding = this.binding.original(); TypeBinding originalType = originalBinding.type; // extra cast needed if field type is type variable if (originalType.leafComponentType().isTypeVariable()) { TypeBinding targetType = (!compileTimeType.isBaseType() && runtimeTimeType.isBaseType()) ? compileTimeType // unboxing: checkcast before conversion : runtimeTimeType; this.genericCast = originalBinding.type.genericCast(targetType); if (this.genericCast instanceof ReferenceBinding) { ReferenceBinding referenceCast = (ReferenceBinding) this.genericCast; if (!referenceCast.canBeSeenBy(scope)) { scope.problemReporter().invalidType(this, new ProblemReferenceBinding( CharOperation.splitOn('.', referenceCast.shortReadableName()), referenceCast, ProblemReasons.NotVisible)); } } } } super.computeConversion(scope, runtimeTimeType, compileTimeType); } public FieldBinding fieldBinding() { return this.binding; } public void generateAssignment(BlockScope currentScope, CodeStream codeStream, Assignment assignment, boolean valueRequired) { int pc = codeStream.position; FieldBinding codegenBinding = this.binding.original(); this.receiver.generateCode(currentScope, codeStream, !codegenBinding.isStatic()); codeStream.recordPositionsFrom(pc, this.sourceStart); assignment.expression.generateCode(currentScope, codeStream, true); fieldStore(currentScope, codeStream, codegenBinding, this.syntheticAccessors == null ? null : this.syntheticAccessors[FieldReference.WRITE], this.actualReceiverType, this.receiver.isImplicitThis(), valueRequired); if (valueRequired) { codeStream.generateImplicitConversion(assignment.implicitConversion); } // no need for generic cast as value got dupped } /** * Field reference 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 (this.constant != Constant.NotAConstant) { if (valueRequired) { codeStream.generateConstant(this.constant, this.implicitConversion); } codeStream.recordPositionsFrom(pc, this.sourceStart); return; } FieldBinding codegenBinding = this.binding.original(); boolean isStatic = codegenBinding.isStatic(); boolean isThisReceiver = this.receiver instanceof ThisReference; Constant fieldConstant = codegenBinding.constant(); if (fieldConstant != Constant.NotAConstant) { if (!isThisReceiver) { this.receiver.generateCode(currentScope, codeStream, !isStatic); if (!isStatic){ codeStream.invokeObjectGetClass(); codeStream.pop(); } } if (valueRequired) { codeStream.generateConstant(fieldConstant, this.implicitConversion); } codeStream.recordPositionsFrom(pc, this.sourceStart); return; } if (valueRequired || (!isThisReceiver && currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) || ((this.implicitConversion & TypeIds.UNBOXING) != 0) || (this.genericCast != null)) { this.receiver.generateCode(currentScope, codeStream, !isStatic); if ((this.bits & NeedReceiverGenericCast) != 0) { codeStream.checkcast(this.actualReceiverType); } pc = codeStream.position; if (codegenBinding.declaringClass == null) { // array length codeStream.arraylength(); if (valueRequired) { codeStream.generateImplicitConversion(this.implicitConversion); } else { // could occur if !valueRequired but compliance >= 1.4 codeStream.pop(); } } else { if (this.syntheticAccessors == null || this.syntheticAccessors[FieldReference.READ] == null) { TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis()); if (isStatic) { codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenBinding, constantPoolDeclaringClass); } else { codeStream.fieldAccess(Opcodes.OPC_getfield, codegenBinding, constantPoolDeclaringClass); } } else { codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[FieldReference.READ], null /* default declaringClass */); } // required cast must occur even if no value is required if (this.genericCast != null) codeStream.checkcast(this.genericCast); if (valueRequired) { codeStream.generateImplicitConversion(this.implicitConversion); } else { boolean isUnboxing = (this.implicitConversion & TypeIds.UNBOXING) != 0; // conversion only generated if unboxing if (isUnboxing) codeStream.generateImplicitConversion(this.implicitConversion); switch (isUnboxing ? postConversionType(currentScope).id : codegenBinding.type.id) { case T_long : case T_double : codeStream.pop2(); break; default : codeStream.pop(); } } } } else { if (isThisReceiver) { if (isStatic){ // if no valueRequired, still need possible side-effects of <clinit> invocation, if field belongs to different class if (TypeBinding.notEquals(this.binding.original().declaringClass, this.actualReceiverType.erasure())) { MethodBinding accessor = this.syntheticAccessors == null ? null : this.syntheticAccessors[FieldReference.READ]; if (accessor == null) { TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis()); codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenBinding, constantPoolDeclaringClass); } else { codeStream.invoke(Opcodes.OPC_invokestatic, accessor, null /* default declaringClass */); } switch (codegenBinding.type.id) { case T_long : case T_double : codeStream.pop2(); break; default : codeStream.pop(); } } } } else { this.receiver.generateCode(currentScope, codeStream, !isStatic); if (!isStatic){ codeStream.invokeObjectGetClass(); // perform null check codeStream.pop(); } } } codeStream.recordPositionsFrom(pc, this.sourceEnd); } public void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired) { boolean isStatic; // check if compound assignment is the only usage of a private field reportOnlyUselesslyReadPrivateField(currentScope, this.binding, valueRequired); FieldBinding codegenBinding = this.binding.original(); this.receiver.generateCode(currentScope, codeStream, !(isStatic = codegenBinding.isStatic())); if (isStatic) { if (this.syntheticAccessors == null || this.syntheticAccessors[FieldReference.READ] == null) { TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis()); codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenBinding, constantPoolDeclaringClass); } else { codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[FieldReference.READ], null /* default declaringClass */); } } else { codeStream.dup(); if (this.syntheticAccessors == null || this.syntheticAccessors[FieldReference.READ] == null) { TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis()); codeStream.fieldAccess(Opcodes.OPC_getfield, codegenBinding, constantPoolDeclaringClass); } else { codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[FieldReference.READ], null /* default declaringClass */); } } int operationTypeID; switch(operationTypeID = (this.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4) { case T_JavaLangString : case T_JavaLangObject : case T_undefined : codeStream.generateStringConcatenationAppend(currentScope, null, expression); break; default : if (this.genericCast != null) codeStream.checkcast(this.genericCast); // promote the array reference to the suitable operation type codeStream.generateImplicitConversion(this.implicitConversion); // generate the increment value (will by itself be promoted to the operation value) if (expression == IntLiteral.One) { // prefix operation codeStream.generateConstant(expression.constant, this.implicitConversion); } else { expression.generateCode(currentScope, codeStream, true); } // perform the operation codeStream.sendOperator(operator, operationTypeID); // cast the value back to the array reference type codeStream.generateImplicitConversion(assignmentImplicitConversion); } fieldStore(currentScope, codeStream, codegenBinding, this.syntheticAccessors == null ? null : this.syntheticAccessors[FieldReference.WRITE], this.actualReceiverType, this.receiver.isImplicitThis(), valueRequired); // no need for generic cast as value got dupped } public void generatePostIncrement(BlockScope currentScope, CodeStream codeStream, CompoundAssignment postIncrement, boolean valueRequired) { boolean isStatic; // check if postIncrement is the only usage of a private field reportOnlyUselesslyReadPrivateField(currentScope, this.binding, valueRequired); FieldBinding codegenBinding = this.binding.original(); this.receiver.generateCode(currentScope, codeStream, !(isStatic = codegenBinding.isStatic())); if (isStatic) { if (this.syntheticAccessors == null || this.syntheticAccessors[FieldReference.READ] == null) { TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis()); codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenBinding, constantPoolDeclaringClass); } else { codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[FieldReference.READ], null /* default declaringClass */); } } else { codeStream.dup(); if (this.syntheticAccessors == null || this.syntheticAccessors[FieldReference.READ] == null) { TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenBinding, this.actualReceiverType, this.receiver.isImplicitThis()); codeStream.fieldAccess(Opcodes.OPC_getfield, codegenBinding, constantPoolDeclaringClass); } else { codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[FieldReference.READ], null /* default declaringClass */); } } TypeBinding operandType; if (this.genericCast != null) { codeStream.checkcast(this.genericCast); operandType = this.genericCast; } else { operandType = codegenBinding.type; } if (valueRequired) { if (isStatic) { switch (operandType.id) { case TypeIds.T_long : case TypeIds.T_double : codeStream.dup2(); break; default : codeStream.dup(); break; } } else { // Stack: [owner][old field value] ---> [old field value][owner][old field value] switch (operandType.id) { case TypeIds.T_long : case TypeIds.T_double : codeStream.dup2_x1(); break; default : codeStream.dup_x1(); break; } } } codeStream.generateImplicitConversion(this.implicitConversion); codeStream.generateConstant( postIncrement.expression.constant, this.implicitConversion); codeStream.sendOperator(postIncrement.operator, this.implicitConversion & TypeIds.COMPILE_TYPE_MASK); codeStream.generateImplicitConversion( postIncrement.preAssignImplicitConversion); fieldStore(currentScope, codeStream, codegenBinding, this.syntheticAccessors == null ? null : this.syntheticAccessors[FieldReference.WRITE], this.actualReceiverType, this.receiver.isImplicitThis(), false); } /** * @see org.eclipse.jdt.internal.compiler.lookup.InvocationSite#genericTypeArguments() */ public TypeBinding[] genericTypeArguments() { return null; } public InferenceContext18 freshInferenceContext(Scope scope) { return null; } public boolean isEquivalent(Reference reference) { // only consider field references relative to "this": if (this.receiver.isThis() && !(this.receiver instanceof QualifiedThisReference)) { // current is a simple "this.f1" char[] otherToken = null; // matching 'reference' could be "f1" or "this.f1": if (reference instanceof SingleNameReference) { otherToken = ((SingleNameReference) reference).token; } else if (reference instanceof FieldReference) { FieldReference fr = (FieldReference) reference; if (fr.receiver.isThis() && !(fr.receiver instanceof QualifiedThisReference)) { otherToken = fr.token; } } return otherToken != null && CharOperation.equals(this.token, otherToken); } else { // search deeper for "this" inside: char[][] thisTokens = getThisFieldTokens(1); if (thisTokens == null) { return false; } // other can be "this.f1.f2", too, or "f1.f2": char[][] otherTokens = null; if (reference instanceof FieldReference) { otherTokens = ((FieldReference) reference).getThisFieldTokens(1); } else if (reference instanceof QualifiedNameReference) { if (((QualifiedNameReference)reference).binding instanceof LocalVariableBinding) return false; // initial variable mismatch: local (from f1.f2) vs. field (from this.f1.f2) otherTokens = ((QualifiedNameReference) reference).tokens; } return CharOperation.equals(thisTokens, otherTokens); } } private char[][] getThisFieldTokens(int nestingCount) { char[][] result = null; if (this.receiver.isThis() && ! (this.receiver instanceof QualifiedThisReference)) { // found an inner-most this-reference, start building the token array: result = new char[nestingCount][]; // fill it front to tail while traveling back out: result[0] = this.token; } else if (this.receiver instanceof FieldReference) { result = ((FieldReference)this.receiver).getThisFieldTokens(nestingCount+1); if (result != null) { // front to tail: outermost is last: result[result.length-nestingCount] = this.token; } } return result; } public boolean isSuperAccess() { return this.receiver.isSuper(); } public boolean isTypeAccess() { return this.receiver != null && this.receiver.isTypeReference(); } public FieldBinding lastFieldBinding() { return this.binding; } /* * No need to emulate access to protected fields since not implicitly accessed */ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo, boolean isReadAccess) { if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) return; // if field from parameterized type got found, use the original field at codegen time FieldBinding codegenBinding = this.binding.original(); if (this.binding.isPrivate()) { if ((TypeBinding.notEquals(currentScope.enclosingSourceType(), codegenBinding.declaringClass)) && this.binding.constant() == Constant.NotAConstant) { if (this.syntheticAccessors == null) this.syntheticAccessors = new MethodBinding[2]; this.syntheticAccessors[isReadAccess ? FieldReference.READ : FieldReference.WRITE] = ((SourceTypeBinding) codegenBinding.declaringClass).addSyntheticMethod(codegenBinding, isReadAccess, false /* not super ref in remote type*/); currentScope.problemReporter().needToEmulateFieldAccess(codegenBinding, this, isReadAccess); return; } } else if (this.receiver instanceof QualifiedSuperReference) { // qualified super // qualified super need emulation always SourceTypeBinding destinationType = (SourceTypeBinding) (((QualifiedSuperReference) this.receiver).currentCompatibleType); if (this.syntheticAccessors == null) this.syntheticAccessors = new MethodBinding[2]; this.syntheticAccessors[isReadAccess ? FieldReference.READ : FieldReference.WRITE] = destinationType.addSyntheticMethod(codegenBinding, isReadAccess, isSuperAccess()); currentScope.problemReporter().needToEmulateFieldAccess(codegenBinding, this, isReadAccess); return; } else if (this.binding.isProtected()) { SourceTypeBinding enclosingSourceType; if (((this.bits & ASTNode.DepthMASK) != 0) && this.binding.declaringClass.getPackage() != (enclosingSourceType = currentScope.enclosingSourceType()).getPackage()) { SourceTypeBinding currentCompatibleType = (SourceTypeBinding) enclosingSourceType.enclosingTypeAt( (this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT); if (this.syntheticAccessors == null) this.syntheticAccessors = new MethodBinding[2]; this.syntheticAccessors[isReadAccess ? FieldReference.READ : FieldReference.WRITE] = currentCompatibleType.addSyntheticMethod(codegenBinding, isReadAccess, isSuperAccess()); currentScope.problemReporter().needToEmulateFieldAccess(codegenBinding, this, isReadAccess); return; } } } public Constant optimizedBooleanConstant() { switch (this.resolvedType.id) { case T_boolean : case T_JavaLangBoolean : return this.constant != Constant.NotAConstant ? this.constant : this.binding.constant(); default : return Constant.NotAConstant; } } /** * @see org.eclipse.jdt.internal.compiler.ast.Expression#postConversionType(Scope) */ public TypeBinding postConversionType(Scope scope) { TypeBinding convertedType = this.resolvedType; if (this.genericCast != null) convertedType = this.genericCast; 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; } public StringBuffer printExpression(int indent, StringBuffer output) { return this.receiver.printExpression(0, output).append('.').append(this.token); } public TypeBinding resolveType(BlockScope scope) { // Answer the signature type of the field. // constants are propaged when the field is final // and initialized with a (compile time) constant //always ignore receiver cast, since may affect constant pool reference boolean receiverCast = false; if (this.receiver instanceof CastExpression) { this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on receiverCast = true; } this.actualReceiverType = this.receiver.resolveType(scope); if (this.actualReceiverType == null) { this.constant = Constant.NotAConstant; return null; } if (receiverCast) { // due to change of declaring class with receiver type, only identity cast should be notified if (TypeBinding.equalsEquals(((CastExpression)this.receiver).expression.resolvedType, this.actualReceiverType)) { scope.problemReporter().unnecessaryCast((CastExpression)this.receiver); } } // the case receiverType.isArrayType and token = 'length' is handled by the scope API FieldBinding fieldBinding = this.binding = scope.getField(this.actualReceiverType, this.token, this); if (!fieldBinding.isValidBinding()) { this.constant = Constant.NotAConstant; if (this.receiver.resolvedType instanceof ProblemReferenceBinding) { // problem already got signaled on receiver, do not report secondary problem return null; } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=245007 avoid secondary errors in case of // missing super type for anonymous classes ... ReferenceBinding declaringClass = fieldBinding.declaringClass; boolean avoidSecondary = declaringClass != null && declaringClass.isAnonymousType() && declaringClass.superclass() instanceof MissingTypeBinding; if (!avoidSecondary) { scope.problemReporter().invalidField(this, this.actualReceiverType); } if (fieldBinding instanceof ProblemFieldBinding) { ProblemFieldBinding problemFieldBinding = (ProblemFieldBinding) fieldBinding; FieldBinding closestMatch = problemFieldBinding.closestMatch; switch(problemFieldBinding.problemId()) { case ProblemReasons.InheritedNameHidesEnclosingName : case ProblemReasons.NotVisible : case ProblemReasons.NonStaticReferenceInConstructorInvocation : case ProblemReasons.NonStaticReferenceInStaticContext : if (closestMatch != null) { fieldBinding = closestMatch; } } } if (!fieldBinding.isValidBinding()) { return null; } } // handle indirect inheritance thru variable secondary bound // receiver may receive generic cast, as part of implicit conversion TypeBinding oldReceiverType = this.actualReceiverType; this.actualReceiverType = this.actualReceiverType.getErasureCompatibleType(fieldBinding.declaringClass); this.receiver.computeConversion(scope, this.actualReceiverType, this.actualReceiverType); if (TypeBinding.notEquals(this.actualReceiverType, oldReceiverType) && TypeBinding.notEquals(this.receiver.postConversionType(scope), this.actualReceiverType)) { // record need for explicit cast at codegen since receiver could not handle it this.bits |= NeedReceiverGenericCast; } if (isFieldUseDeprecated(fieldBinding, scope, this.bits)) { scope.problemReporter().deprecatedField(fieldBinding, this); } boolean isImplicitThisRcv = this.receiver.isImplicitThis(); this.constant = isImplicitThisRcv ? fieldBinding.constant() : Constant.NotAConstant; if (fieldBinding.isStatic()) { // static field accessed through receiver? legal but unoptimal (optional warning) if (!(isImplicitThisRcv || (this.receiver instanceof NameReference && (((NameReference) this.receiver).bits & Binding.TYPE) != 0))) { scope.problemReporter().nonStaticAccessToStaticField(this, fieldBinding); } ReferenceBinding declaringClass = this.binding.declaringClass; if (!isImplicitThisRcv && TypeBinding.notEquals(declaringClass, this.actualReceiverType) && declaringClass.canBeSeenBy(scope)) { scope.problemReporter().indirectAccessToStaticField(this, fieldBinding); } // check if accessing enum static field in initializer if (declaringClass.isEnum()) { MethodScope methodScope = scope.methodScope(); SourceTypeBinding sourceType = scope.enclosingSourceType(); if (this.constant == Constant.NotAConstant && !methodScope.isStatic && (TypeBinding.equalsEquals(sourceType, declaringClass) || TypeBinding.equalsEquals(sourceType.superclass, declaringClass)) // enum constant body && methodScope.isInsideInitializerOrConstructor()) { scope.problemReporter().enumStaticFieldUsedDuringInitialization(this.binding, this); } } } TypeBinding fieldType = fieldBinding.type; if (fieldType != null) { if ((this.bits & ASTNode.IsStrictlyAssigned) == 0) { fieldType = fieldType.capture(scope, this.sourceEnd); // perform capture conversion if read access } this.resolvedType = fieldType; if ((fieldType.tagBits & TagBits.HasMissingType) != 0) { scope.problemReporter().invalidType(this, fieldType); return null; } } return fieldType; } public void setActualReceiverType(ReferenceBinding receiverType) { this.actualReceiverType = receiverType; } public void setDepth(int depth) { this.bits &= ~ASTNode.DepthMASK; // flush previous depth if any if (depth > 0) { this.bits |= (depth & 0xFF) << ASTNode.DepthSHIFT; // encoded on 8 bits } } public void setFieldIndex(int index) { // ignored } public void traverse(ASTVisitor visitor, BlockScope scope) { if (visitor.visit(this, scope)) { this.receiver.traverse(visitor, scope); } visitor.endVisit(this, scope); } public VariableBinding nullAnnotatedVariableBinding(boolean supportTypeAnnotations) { if (this.binding != null) { if (supportTypeAnnotations || ((this.binding.tagBits & TagBits.AnnotationNullMASK) != 0)) { return this.binding; } } return null; } }