Skip to content

Commit

Permalink
Implemented support for string concatenation compound assignments inv…
Browse files Browse the repository at this point in the history
…olving ReadOnlySpan<char>.
  • Loading branch information
siegfriedpammer committed Jul 21, 2024
1 parent 1c71f6a commit 7b1f8a3
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4719,12 +4719,14 @@ private static void Issue1082(string[] strings, List<char> chars, bool flag, int
}
#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)
Expand Down
32 changes: 25 additions & 7 deletions ICSharpCode.Decompiler/CSharp/CallBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,15 +247,11 @@ private ExpressionWithResolveResult BuildStringConcat(IMethod method, List<(ILIn
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;
}
Expand Down Expand Up @@ -283,7 +279,29 @@ private bool IsSpanBasedStringConcat(CallInstruction call, out List<(ILInstructi
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"
Expand Down
31 changes: 27 additions & 4 deletions ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1799,7 +1799,17 @@ TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorTy

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)
{
Expand All @@ -1821,11 +1831,24 @@ protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(
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)
{
Expand Down
13 changes: 12 additions & 1 deletion ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using System.Linq;
using System.Linq.Expressions;

using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;

Expand Down Expand Up @@ -452,7 +453,17 @@ static ExpressionType ToCompound(ExpressionType from)
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);
Expand Down

0 comments on commit 7b1f8a3

Please sign in to comment.