Browse Source

Implemented support for string concatenation compound assignments involving ReadOnlySpan<char>.

pull/3243/head
Siegfried Pammer 10 months ago
parent
commit
7b1f8a305c
  1. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  2. 32
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  3. 31
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 13
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs

@ -4719,12 +4719,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4719,12 +4719,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
#endif
private static void StringPropertyCompoundAssign()
private static void StringPropertyCompoundAssign(char c)
{
StaticStringProperty += "a";
StaticStringProperty += 1;
StaticStringProperty += c;
new CustomClass().StringProp += "a";
new CustomClass().StringProp += 1;
new CustomClass().StringProp += c;
}
public uint PreIncrementIndexer(string name)

32
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -247,15 +247,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -247,15 +247,11 @@ namespace ICSharpCode.Decompiler.CSharp
return result;
}
private bool IsSpanBasedStringConcat(CallInstruction call, out List<(ILInstruction, KnownTypeCode)> operands)
static bool IsSpanBasedStringConcat(CallInstruction call, out List<(ILInstruction, KnownTypeCode)> operands)
{
operands = null;
if (call.Method is not { Name: "Concat", IsStatic: true })
{
return false;
}
if (!call.Method.DeclaringType.IsKnownType(KnownTypeCode.String))
if (!IsSpanBasedStringConcat(call.Method))
{
return false;
}
@ -283,7 +279,29 @@ namespace ICSharpCode.Decompiler.CSharp @@ -283,7 +279,29 @@ namespace ICSharpCode.Decompiler.CSharp
return call.Arguments.Count >= 2 && firstStringArgumentIndex <= 1;
}
private bool IsStringToReadOnlySpanCharImplicitConversion(IMethod method)
internal static bool IsSpanBasedStringConcat(IMethod method)
{
if (method is not { Name: "Concat", IsStatic: true })
{
return false;
}
if (!method.DeclaringType.IsKnownType(KnownTypeCode.String))
{
return false;
}
foreach (var p in method.Parameters)
{
if (!p.Type.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
return false;
if (!p.Type.TypeArguments[0].IsKnownType(KnownTypeCode.Char))
return false;
}
return true;
}
internal static bool IsStringToReadOnlySpanCharImplicitConversion(IMethod method)
{
return method.IsOperator
&& method.Name == "op_Implicit"

31
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1799,7 +1799,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1799,7 +1799,17 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, TranslationContext context)
{
IType loadType = inst.Method.Parameters[0].Type;
IType loadType;
bool isSpanBasedStringConcat = CallBuilder.IsSpanBasedStringConcat(inst.Method);
if (isSpanBasedStringConcat)
{
loadType = typeSystem.FindType(KnownTypeCode.String);
}
else
{
loadType = inst.Method.Parameters[0].Type;
}
ExpressionWithResolveResult target;
if (inst.TargetKind == CompoundTargetKind.Address)
{
@ -1821,11 +1831,24 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1821,11 +1831,24 @@ namespace ICSharpCode.Decompiler.CSharp
if (UserDefinedCompoundAssign.IsStringConcat(inst.Method))
{
Debug.Assert(inst.Method.Parameters.Count == 2);
var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true);
var valueExpr = ReplaceMethodCallsWithOperators.RemoveRedundantToStringInConcat(value, inst.Method, isLastArgument: true).Detach();
Expression valueExpr;
ResolveResult valueResolveResult;
if (isSpanBasedStringConcat && inst.Value is NewObj { Arguments: [AddressOf addressOf] })
{
IType charType = typeSystem.FindType(KnownTypeCode.Char);
var value = Translate(addressOf.Value, charType).ConvertTo(charType, this);
valueExpr = value.Expression;
valueResolveResult = value.ResolveResult;
}
else
{
var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true);
valueExpr = ReplaceMethodCallsWithOperators.RemoveRedundantToStringInConcat(value, inst.Method, isLastArgument: true).Detach();
valueResolveResult = value.ResolveResult;
}
return new AssignmentExpression(target, AssignmentOperatorType.Add, valueExpr)
.WithILInstruction(inst)
.WithRR(new OperatorResolveResult(inst.Method.ReturnType, ExpressionType.AddAssign, inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult }));
.WithRR(new OperatorResolveResult(inst.Method.ReturnType, ExpressionType.AddAssign, inst.Method, inst.IsLifted, new[] { target.ResolveResult, valueResolveResult }));
}
else if (inst.Method.Parameters.Count == 2)
{

13
ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

@ -21,6 +21,7 @@ using System.Diagnostics; @@ -21,6 +21,7 @@ using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
@ -452,7 +453,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -452,7 +453,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; // for now we only support binary compound assignments
if (!targetType.IsKnownType(KnownTypeCode.String))
return false;
if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable))
var arg = concatCall.Arguments[0];
if (arg is Call call && CallBuilder.IsStringToReadOnlySpanCharImplicitConversion(call.Method))
{
arg = call.Arguments[0];
if (!(concatCall.Arguments[1] is NewObj { Arguments: [AddressOf addressOf] } newObj) || !ILInlining.IsReadOnlySpanCharCtor(newObj.Method))
{
return false;
}
}
if (!IsMatchingCompoundLoad(arg, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable))
return false;
context.Step($"Compound assignment (string concatenation)", compoundStore);
finalizeMatch?.Invoke(context);

Loading…
Cancel
Save