public void generatePostIncrement( BlockScope currentScope, CompoundAssignment postIncrement, boolean valueRequired) { switch (this.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // assigning to a field FieldBinding fieldBinding = (FieldBinding) this.binding; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 // check if postIncrement is the only usage of a private field reportOnlyUselesslyReadPrivateField(currentScope, fieldBinding, valueRequired); FieldBinding codegenField = fieldBinding.original(); fieldStore( currentScope, codegenField, this.syntheticAccessors == null ? null : this.syntheticAccessors[SingleNameReference.WRITE], this.actualReceiverType, true /*implicit this*/, false); // no need for generic cast return; case Binding.LOCAL: // assigning to a local variable LocalVariableBinding localBinding = (LocalVariableBinding) this.binding; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 // check if postIncrement is the only usage of this local Reference.reportOnlyUselesslyReadLocal(currentScope, localBinding, valueRequired); if (localBinding.resolvedPosition == -1) { if (valueRequired) { // restart code gen localBinding.useFlag = LocalVariableBinding.USED; throw new AbortMethod(null, null); } } } }
public void generateCode(BlockScope currentScope, boolean valueRequired) { if (this.constant != Constant.NotAConstant) { return; } else { switch (this.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // reading a field FieldBinding codegenField = ((FieldBinding) this.binding).original(); Constant fieldConstant = codegenField.constant(); if (fieldConstant != Constant.NotAConstant) { return; } if (codegenField.isStatic()) { if (!valueRequired // if no valueRequired, still need possible side-effects of <clinit> invocation, // if field belongs to different class && ((FieldBinding) this.binding).original().declaringClass == this.actualReceiverType.erasure() && ((this.implicitConversion & TypeIds.UNBOXING) == 0) && this.genericCast == null) { // if no valueRequired, optimize out entire gen return; } } else { if (!valueRequired && (this.implicitConversion & TypeIds.UNBOXING) == 0 && this.genericCast == null) { // if no valueRequired, optimize out entire gen return; } } break; case Binding.LOCAL: // reading a local LocalVariableBinding localBinding = (LocalVariableBinding) this.binding; if (localBinding.resolvedPosition == -1) { if (valueRequired) { // restart code gen localBinding.useFlag = LocalVariableBinding.USED; throw new AbortMethod(null, null); } return; } if (!valueRequired && (this.implicitConversion & TypeIds.UNBOXING) == 0) { // if no valueRequired, optimize out entire gen return; } break; default: // type return; } } }
public FlowInfo analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, boolean valueRequired) { switch (this.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // reading a field if (valueRequired || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) { manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/); } // check if reading a final blank field FieldBinding fieldBinding = (FieldBinding) this.binding; if (fieldBinding.isBlankFinal() && currentScope.needBlankFinalFieldInitializationCheck(fieldBinding)) { FlowInfo fieldInits = flowContext.getInitsForFinalBlankInitializationCheck( fieldBinding.declaringClass.original(), flowInfo); if (!fieldInits.isDefinitelyAssigned(fieldBinding)) { currentScope.problemReporter().uninitializedBlankFinalField(fieldBinding, this); } } if (!fieldBinding.isStatic()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682 currentScope.resetEnclosingMethodStaticFlag(); } break; case Binding.LOCAL: // reading a local variable LocalVariableBinding localBinding; if (!flowInfo.isDefinitelyAssigned(localBinding = (LocalVariableBinding) this.binding)) { currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); } if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { localBinding.useFlag = LocalVariableBinding.USED; } else if (localBinding.useFlag == LocalVariableBinding.UNUSED) { localBinding.useFlag = LocalVariableBinding.FAKE_USED; } } if (valueRequired) { manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); } return flowInfo; }
public FlowInfo analyseAssignment( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, Assignment assignment, boolean isCompound) { boolean isReachable = (flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0; // compound assignment extra work if (isCompound) { // check the variable part is initialized if blank final switch (this.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // reading a field FieldBinding fieldBinding = (FieldBinding) this.binding; if (fieldBinding.isBlankFinal() && currentScope.needBlankFinalFieldInitializationCheck(fieldBinding)) { FlowInfo fieldInits = flowContext.getInitsForFinalBlankInitializationCheck( fieldBinding.declaringClass.original(), flowInfo); if (!fieldInits.isDefinitelyAssigned(fieldBinding)) { currentScope.problemReporter().uninitializedBlankFinalField(fieldBinding, this); } } if (!fieldBinding.isStatic()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682 currentScope.resetEnclosingMethodStaticFlag(); } manageSyntheticAccessIfNecessary(currentScope, flowInfo, true /*read-access*/); break; case Binding.LOCAL: // reading a local variable // check if assigning a final blank field LocalVariableBinding localBinding; if (!flowInfo.isDefinitelyAssigned(localBinding = (LocalVariableBinding) this.binding)) { currentScope.problemReporter().uninitializedLocalVariable(localBinding, this); // we could improve error msg here telling "cannot use compound assignment on final // local variable" } if (localBinding.useFlag != LocalVariableBinding.USED) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=185682 // access from compound assignment does not prevent "unused" warning, unless unboxing is // involved: if (isReachable && (this.implicitConversion & TypeIds.UNBOXING) != 0) { localBinding.useFlag = LocalVariableBinding.USED; } else { // use values < 0 to count the number of compound uses: if (localBinding.useFlag <= LocalVariableBinding.UNUSED) localBinding.useFlag--; } } } } if (assignment.expression != null) { flowInfo = assignment .expression .analyseCode(currentScope, flowContext, flowInfo) .unconditionalInits(); } switch (this.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // assigning to a field manageSyntheticAccessIfNecessary(currentScope, flowInfo, false /*write-access*/); // check if assigning a final field FieldBinding fieldBinding = (FieldBinding) this.binding; if (fieldBinding.isFinal()) { // inside a context where allowed if (!isCompound && fieldBinding.isBlankFinal() && currentScope.allowBlankFinalFieldAssignment(fieldBinding)) { if (flowInfo.isPotentiallyAssigned(fieldBinding)) { currentScope .problemReporter() .duplicateInitializationOfBlankFinalField(fieldBinding, this); } else { flowContext.recordSettingFinal(fieldBinding, this, flowInfo); } flowInfo.markAsDefinitelyAssigned(fieldBinding); } else { currentScope.problemReporter().cannotAssignToFinalField(fieldBinding, this); } } if (!fieldBinding.isStatic()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682 currentScope.resetEnclosingMethodStaticFlag(); } break; case Binding.LOCAL: // assigning to a local variable LocalVariableBinding localBinding = (LocalVariableBinding) this.binding; if (!flowInfo.isDefinitelyAssigned(localBinding)) { // for local variable debug attributes this.bits |= ASTNode.FirstAssignmentToLocal; } else { this.bits &= ~ASTNode.FirstAssignmentToLocal; } if (localBinding.isFinal()) { if ((this.bits & ASTNode.DepthMASK) == 0) { // tolerate assignment to final local in unreachable code (45674) if ((isReachable && isCompound) || !localBinding.isBlankFinal()) { currentScope.problemReporter().cannotAssignToFinalLocal(localBinding, this); } else if (flowInfo.isPotentiallyAssigned(localBinding)) { currentScope .problemReporter() .duplicateInitializationOfFinalLocal(localBinding, this); } else { flowContext.recordSettingFinal(localBinding, this, flowInfo); } } else { currentScope.problemReporter().cannotAssignToFinalOuterLocal(localBinding, this); } } else /* avoid double diagnostic */ if ((localBinding.tagBits & TagBits.IsArgument) != 0) { currentScope.problemReporter().parameterAssignment(localBinding, this); } flowInfo.markAsDefinitelyAssigned(localBinding); } manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); return flowInfo; }
/* * The APIs with an extra argument is used whenever there are two references to the same variable which * are optimized in one access: e.g "a = a + 1" optimized into "a++". */ public void generateCompoundAssignment( BlockScope currentScope, MethodBinding writeAccessor, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired) { switch (this.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // assigning to a field break; case Binding.LOCAL: // assigning to a local variable (cannot assign to outer local) LocalVariableBinding localBinding = (LocalVariableBinding) this.binding; // using incr bytecode if possible Constant assignConstant; switch (localBinding.type.id) { case T_JavaLangString: return; case T_int: assignConstant = expression.constant; if (localBinding.resolvedPosition == -1) { if (valueRequired) { /* * restart code gen because we either: * - need the value * - the constant can have potential side-effect */ localBinding.useFlag = LocalVariableBinding.USED; throw new AbortMethod(null, null); } else if (assignConstant == Constant.NotAConstant) { // we only need to generate the value of the expression's constant if it is not a // constant expression expression.generateCode(currentScope, false); } return; } if ((assignConstant != Constant.NotAConstant) && (assignConstant.typeID() != TypeIds.T_float) // only for integral types && (assignConstant.typeID() != TypeIds.T_double)) { // TODO (philippe) is this test needed ? switch (operator) { case PLUS: int increment = assignConstant.intValue(); if (increment != (short) increment) break; // not representable as a 16-bits value return; case MINUS: increment = -assignConstant.intValue(); if (increment != (short) increment) break; // not representable as a 16-bits value return; } } // $FALL-THROUGH$ default: if (localBinding.resolvedPosition == -1) { assignConstant = expression.constant; if (valueRequired) { /* * restart code gen because we either: * - need the value * - the constant can have potential side-effect */ localBinding.useFlag = LocalVariableBinding.USED; throw new AbortMethod(null, null); } else if (assignConstant == Constant.NotAConstant) { // we only need to generate the value of the expression's constant if it is not a // constant expression expression.generateCode(currentScope, false); } return; } } } switch ((this.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4) { case T_JavaLangString: case T_JavaLangObject: case T_undefined: // we enter here if the single name reference is a field of type java.lang.String or if the // type of the // operation is java.lang.Object // For example: o = o + ""; // where the compiled type of o is java.lang.Object. // no need for generic cast on previous #getfield since using Object string buffer methods. break; default: // promote the array reference to the suitable operation type // generate the increment value (will by itself be promoted to the operation value) if (expression != IntLiteral.One) { // prefix operation expression.generateCode(currentScope, true); } } // store the result back into the variable switch (this.bits & ASTNode.RestrictiveFlagMASK) { case Binding.FIELD: // assigning to a field FieldBinding codegenField = ((FieldBinding) this.binding).original(); fieldStore( currentScope, codegenField, writeAccessor, this.actualReceiverType, true /* implicit this*/, valueRequired); // no need for generic cast as value got dupped return; case Binding.LOCAL: // assigning to a local variable LocalVariableBinding localBinding = (LocalVariableBinding) this.binding; if (valueRequired) { switch (localBinding.type.id) { case TypeIds.T_long: case TypeIds.T_double: break; default: break; } } } }