Browse Source

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

pull/3243/head
Siegfried Pammer 1 year 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
} }
#endif #endif
private static void StringPropertyCompoundAssign() private static void StringPropertyCompoundAssign(char c)
{ {
StaticStringProperty += "a"; StaticStringProperty += "a";
StaticStringProperty += 1; StaticStringProperty += 1;
StaticStringProperty += c;
new CustomClass().StringProp += "a"; new CustomClass().StringProp += "a";
new CustomClass().StringProp += 1; new CustomClass().StringProp += 1;
new CustomClass().StringProp += c;
} }
public uint PreIncrementIndexer(string name) public uint PreIncrementIndexer(string name)

32
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -247,15 +247,11 @@ namespace ICSharpCode.Decompiler.CSharp
return result; return result;
} }
private bool IsSpanBasedStringConcat(CallInstruction call, out List<(ILInstruction, KnownTypeCode)> operands) static bool IsSpanBasedStringConcat(CallInstruction call, out List<(ILInstruction, KnownTypeCode)> operands)
{ {
operands = null; operands = null;
if (call.Method is not { Name: "Concat", IsStatic: true }) if (!IsSpanBasedStringConcat(call.Method))
{
return false;
}
if (!call.Method.DeclaringType.IsKnownType(KnownTypeCode.String))
{ {
return false; return false;
} }
@ -283,7 +279,29 @@ namespace ICSharpCode.Decompiler.CSharp
return call.Arguments.Count >= 2 && firstStringArgumentIndex <= 1; 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 return method.IsOperator
&& method.Name == "op_Implicit" && method.Name == "op_Implicit"

31
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1799,7 +1799,17 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, TranslationContext context) 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; ExpressionWithResolveResult target;
if (inst.TargetKind == CompoundTargetKind.Address) if (inst.TargetKind == CompoundTargetKind.Address)
{ {
@ -1821,11 +1831,24 @@ namespace ICSharpCode.Decompiler.CSharp
if (UserDefinedCompoundAssign.IsStringConcat(inst.Method)) if (UserDefinedCompoundAssign.IsStringConcat(inst.Method))
{ {
Debug.Assert(inst.Method.Parameters.Count == 2); Debug.Assert(inst.Method.Parameters.Count == 2);
var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true); Expression valueExpr;
var valueExpr = ReplaceMethodCallsWithOperators.RemoveRedundantToStringInConcat(value, inst.Method, isLastArgument: true).Detach(); 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) return new AssignmentExpression(target, AssignmentOperatorType.Add, valueExpr)
.WithILInstruction(inst) .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) else if (inst.Method.Parameters.Count == 2)
{ {

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

@ -21,6 +21,7 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
@ -452,7 +453,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; // for now we only support binary compound assignments return false; // for now we only support binary compound assignments
if (!targetType.IsKnownType(KnownTypeCode.String)) if (!targetType.IsKnownType(KnownTypeCode.String))
return false; 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; return false;
context.Step($"Compound assignment (string concatenation)", compoundStore); context.Step($"Compound assignment (string concatenation)", compoundStore);
finalizeMatch?.Invoke(context); finalizeMatch?.Invoke(context);

Loading…
Cancel
Save