@ -276,18 +276,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -276,18 +276,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
ILInstruction newInst ;
if ( UnwrapSmallIntegerConv ( setterValue , out var smallIntConv ) is BinaryNumericInstruction binary ) {
if ( ! IsMatchingCompoundLoad ( binary . Left , compoundStore , forbiddenVariable : storeInSetter ? . Variable ) )
if ( ! IsMatchingCompoundLoad ( binary . Left , compoundStore , out var target , out var targetKind , forbiddenVariable : storeInSetter ? . Variable ) )
return false ;
if ( ! ValidateCompoundAssign ( binary , smallIntConv , targetType ) )
return false ;
context . Step ( $"Compound assignment (binary.numeric)" , compoundStore ) ;
newInst = new NumericCompoundAssign (
binary , binary . Left , binary . Right ,
targetType , CompoundAssignmentTyp e . EvaluatesToNewValue ) ;
binary , target , targetKind , binary . Right ,
targetType , CompoundEvalMod e . EvaluatesToNewValue ) ;
} else if ( setterValue is Call operatorCall & & operatorCall . Method . IsOperator ) {
if ( operatorCall . Arguments . Count = = 0 )
return false ;
if ( ! IsMatchingCompoundLoad ( operatorCall . Arguments [ 0 ] , compoundStore , forbiddenVariable : storeInSetter ? . Variable ) )
if ( ! IsMatchingCompoundLoad ( operatorCall . Arguments [ 0 ] , compoundStore , out var target , out var targetKind , forbiddenVariable : storeInSetter ? . Variable ) )
return false ;
ILInstruction rhs ;
if ( operatorCall . Arguments . Count = = 2 ) {
@ -305,24 +305,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -305,24 +305,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if ( operatorCall . IsLifted )
return false ; // TODO: add tests and think about whether nullables need special considerations
context . Step ( $"Compound assignment (user-defined binary)" , compoundStore ) ;
newInst = new UserDefinedCompoundAssign ( operatorCall . Method , CompoundAssignmentTyp e . EvaluatesToNewValue ,
operatorCall . Arguments [ 0 ] , rhs ) ;
newInst = new UserDefinedCompoundAssign ( operatorCall . Method , CompoundEvalMod e . EvaluatesToNewValue ,
target , targetKind , rhs ) ;
} else if ( setterValue is DynamicBinaryOperatorInstruction dynamicBinaryOp ) {
if ( ! IsMatchingCompoundLoad ( dynamicBinaryOp . Left , compoundStore , forbiddenVariable : storeInSetter ? . Variable ) )
if ( ! IsMatchingCompoundLoad ( dynamicBinaryOp . Left , compoundStore , out var target , out var targetKind , forbiddenVariable : storeInSetter ? . Variable ) )
return false ;
context . Step ( $"Compound assignment (dynamic binary)" , compoundStore ) ;
newInst = new DynamicCompoundAssign ( dynamicBinaryOp . Operation , dynamicBinaryOp . BinderFlags , dynamicBinaryOp . Lef t, dynamicBinaryOp . LeftArgumentInfo , dynamicBinaryOp . Right , dynamicBinaryOp . RightArgumentInfo ) ;
newInst = new DynamicCompoundAssign ( dynamicBinaryOp . Operation , dynamicBinaryOp . BinderFlags , targe t, dynamicBinaryOp . LeftArgumentInfo , dynamicBinaryOp . Right , dynamicBinaryOp . RightArgumentInfo , targetKind ) ;
} else if ( setterValue is Call concatCall & & UserDefinedCompoundAssign . IsStringConcat ( concatCall . Method ) ) {
// setterValue is a string.Concat() invocation
if ( concatCall . Arguments . Count ! = 2 )
return false ; // for now we only support binary compound assignments
if ( ! targetType . IsKnownType ( KnownTypeCode . String ) )
return false ;
if ( ! IsMatchingCompoundLoad ( concatCall . Arguments [ 0 ] , compoundStore , forbiddenVariable : storeInSetter ? . Variable ) )
if ( ! IsMatchingCompoundLoad ( concatCall . Arguments [ 0 ] , compoundStore , out var target , out var targetKind , forbiddenVariable : storeInSetter ? . Variable ) )
return false ;
context . Step ( $"Compound assignment (string concatenation)" , compoundStore ) ;
newInst = new UserDefinedCompoundAssign ( concatCall . Method , CompoundAssignmentTyp e . EvaluatesToNewValue ,
concatCall . Arguments [ 0 ] , concatCall . Arguments [ 1 ] ) ;
newInst = new UserDefinedCompoundAssign ( concatCall . Method , CompoundEvalMod e . EvaluatesToNewValue ,
target , targetKind , concatCall . Arguments [ 1 ] ) ;
} else {
return false ;
}
@ -469,7 +469,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -469,7 +469,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Gets whether 'inst' is a possible store for use as a compound store.
/// </summary>
static bool IsCompoundStore ( ILInstruction inst , out IType storeType , out ILInstruction value , ICompilation compilation )
/// <remarks>
/// Output parameters:
/// storeType: The type of the value being stored.
/// value: The value being stored (will be analyzed further to detect compound assignments)
///
/// Every IsCompoundStore() call should be followed by an IsMatchingCompoundLoad() call.
/// </remarks>
static bool IsCompoundStore ( ILInstruction inst , out IType storeType ,
out ILInstruction value , ICompilation compilation )
{
value = null ;
storeType = null ;
@ -511,18 +519,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -511,18 +519,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
static bool IsMatchingCompoundLoad ( ILInstruction load , ILInstruction store , ILVariable forbiddenVariable )
static bool IsMatchingCompoundLoad ( ILInstruction load , ILInstruction store ,
out ILInstruction target , out CompoundTargetKind targetKind ,
ILVariable forbiddenVariable = null )
{
target = null ;
targetKind = 0 ;
if ( load is LdObj ldobj & & store is StObj stobj ) {
Debug . Assert ( SemanticHelper . IsPure ( stobj . Target . Flags ) ) ;
if ( ! SemanticHelper . IsPure ( ldobj . Target . Flags ) )
return false ;
if ( forbiddenVariable ! = null & & forbiddenVariable . IsUsedWithin ( ldobj . Target ) )
return false ;
target = ldobj . Target ;
targetKind = CompoundTargetKind . Address ;
return ldobj . Target . Match ( stobj . Target ) . Success ;
} else if ( MatchingGetterAndSetterCalls ( load as CallInstruction , store as CallInstruction ) ) {
if ( forbiddenVariable ! = null & & forbiddenVariable . IsUsedWithin ( load ) )
return false ;
target = load ;
targetKind = CompoundTargetKind . Property ;
return true ;
} else {
return false ;
@ -574,18 +590,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -574,18 +590,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false ;
if ( ! ( stloc . Variable . Kind = = VariableKind . Local | | stloc . Variable . Kind = = VariableKind . StackSlot ) )
return false ;
if ( ! IsMatchingCompoundLoad ( stloc . Value , store , stloc . Variable ) )
if ( ! IsMatchingCompoundLoad ( stloc . Value , store , out var target , out var targetKind , forbiddenVariable : stloc . Variable ) )
return false ;
if ( IsImplicitTruncation ( stloc . Value , stloc . Variable . Type , context . TypeSystem ) )
return false ;
context . Step ( "TransformPostIncDecOperatorWithInlineStore" , store ) ;
if ( binary ! = null ) {
block . Instructions [ pos ] = new StLoc ( stloc . Variable , new NumericCompoundAssign (
binary , stloc . Value , binary . Right , targetType , CompoundAssignmentTyp e . EvaluatesToOldValue ) ) ;
binary , target , targetKind , binary . Right , targetType , CompoundEvalMod e . EvaluatesToOldValue ) ) ;
} else {
Call operatorCall = ( Call ) value ;
block . Instructions [ pos ] = new StLoc ( stloc . Variable , new UserDefinedCompoundAssign (
operatorCall . Method , CompoundAssignmentTyp e . EvaluatesToOldValue , stloc . Value , new LdcI4 ( 1 ) ) ) ;
operatorCall . Method , CompoundEvalMod e . EvaluatesToOldValue , target , targetKind , new LdcI4 ( 1 ) ) ) ;
}
return true ;
}
@ -612,7 +628,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -612,7 +628,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// 'stloc l' is implicitly truncating the value
return false ;
}
if ( ! IsMatchingCompoundLoad ( inst . Value , store , inst . Variable ) )
if ( ! IsMatchingCompoundLoad ( inst . Value , store , out var target , out var targetKind , forbiddenVariable : inst . Variable ) )
return false ;
if ( UnwrapSmallIntegerConv ( value , out var conv ) is BinaryNumericInstruction binary ) {
if ( ! binary . Left . MatchLdLoc ( inst . Variable ) | | ! binary . Right . MatchLdcI ( 1 ) )
@ -622,8 +638,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -622,8 +638,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if ( ! ValidateCompoundAssign ( binary , conv , targetType ) )
return false ;
context . Step ( "TransformPostIncDecOperator (builtin)" , inst ) ;
inst . Value = new NumericCompoundAssign ( binary , inst . Value , binary . Right ,
targetType , CompoundAssignmentTyp e . EvaluatesToOldValue ) ;
inst . Value = new NumericCompoundAssign ( binary , target , targetKind , binary . Right ,
targetType , CompoundEvalMod e . EvaluatesToOldValue ) ;
} else if ( value is Call operatorCall & & operatorCall . Method . IsOperator & & operatorCall . Arguments . Count = = 1 ) {
if ( ! operatorCall . Arguments [ 0 ] . MatchLdLoc ( inst . Variable ) )
return false ;
@ -633,7 +649,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
@@ -633,7 +649,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false ; // TODO: add tests and think about whether nullables need special considerations
context . Step ( "TransformPostIncDecOperator (user-defined)" , inst ) ;
inst . Value = new UserDefinedCompoundAssign ( operatorCall . Method ,
CompoundAssignmentTyp e . EvaluatesToOldValue , inst . Value , new LdcI4 ( 1 ) ) ;
CompoundEvalMod e . EvaluatesToOldValue , target , targetKind , new LdcI4 ( 1 ) ) ;
} else {
return false ;
}