* are optimized in one access: e.g "a = a + 1" optimized into "a++".
*/
public void generateCompoundAssignment(BlockScope currentScope, CodeStream codeStream, MethodBinding writeAccessor, Expression expression, int operator, int assignmentImplicitConversion, boolean valueRequired) {
switch (this.bits & ASTNode.RestrictiveFlagMASK) {
case Binding.FIELD : // assigning to a field
FieldBinding codegenField = ((FieldBinding) this.binding).original();
if (codegenField.isStatic()) {
if ((this.syntheticAccessors == null) || (this.syntheticAccessors[SingleNameReference.READ] == null)) {
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenField, this.actualReceiverType, true /* implicit this */);
codeStream.fieldAccess(Opcodes.OPC_getstatic, codegenField, constantPoolDeclaringClass);
} else {
codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[SingleNameReference.READ], null /* default declaringClass */);
}
} else {
if ((this.bits & ASTNode.DepthMASK) != 0) {
ReferenceBinding targetType = currentScope.enclosingSourceType().enclosingTypeAt((this.bits & ASTNode.DepthMASK) >> ASTNode.DepthSHIFT);
Object[] emulationPath = currentScope.getEmulationPath(targetType, true /*only exact match*/, false/*consider enclosing arg*/);
codeStream.generateOuterAccess(emulationPath, this, targetType, currentScope);
} else {
codeStream.aload_0();
}
codeStream.dup();
if ((this.syntheticAccessors == null) || (this.syntheticAccessors[SingleNameReference.READ] == null)) {
TypeBinding constantPoolDeclaringClass = CodeStream.getConstantPoolDeclaringClass(currentScope, codegenField, this.actualReceiverType, true /* implicit this */);
codeStream.fieldAccess(Opcodes.OPC_getfield, codegenField, constantPoolDeclaringClass);
} else {
codeStream.invoke(Opcodes.OPC_invokestatic, this.syntheticAccessors[SingleNameReference.READ], null /* default declaringClass */);
}
}
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 :
codeStream.generateStringConcatenationAppend(currentScope, this, expression);
if (valueRequired) {
codeStream.dup();
}
codeStream.store(localBinding, false);
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(CodeStream.RESTART_CODE_GEN_FOR_UNUSED_LOCALS_MODE, 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, codeStream, 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
codeStream.iinc(localBinding.resolvedPosition, increment);
if (valueRequired) {
codeStream.load(localBinding);
}
return;
case MINUS :
increment = -assignConstant.intValue();
if (increment != (short) increment) break; // not representable as a 16-bits value
codeStream.iinc(localBinding.resolvedPosition, increment);
if (valueRequired) {
codeStream.load(localBinding);
}
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(CodeStream.RESTART_CODE_GEN_FOR_UNUSED_LOCALS_MODE, 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, codeStream, false);
}
return;
}
codeStream.load(localBinding);
}
}
// perform the actual compound operation
int operationTypeID;
switch(operationTypeID = (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.
codeStream.generateStringConcatenationAppend(currentScope, null, expression);
// 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
if (this.genericCast != null)
codeStream.checkcast(this.genericCast);
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);
}
// 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, codeStream, 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;