Browse Source

Merge pull request #3380 from ds5678/runtimehelpers-createspan-array-initialization

Replace `RuntimeHelpers.CreateSpan<T>(LdMemberToken)` with `new T[] { }`
pull/3410/head
Siegfried Pammer 5 months ago committed by GitHub
parent
commit
180428a1ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 14
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs
  2. 6
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  3. 88
      ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

14
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs

@ -447,6 +447,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests @@ -447,6 +447,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
#endif
#if CS110 && !NET40
public static ReadOnlySpan<byte> UTF8Literal => "Hello, world!"u8;
public static ReadOnlySpan<byte> UTF8LiteralWithNullTerminator => "Hello, world!\0"u8;
#endif
#endregion
@ -805,6 +806,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests @@ -805,6 +806,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
array[0] = 1;
Console.WriteLine(array.Length);
}
#if !NET40 && CS70
public static ReadOnlySpan<byte> ReadOnlySpanInitializer_ByteArray()
{
return new byte[3] { 1, 2, 3 };
}
public static ReadOnlySpan<int> ReadOnlySpanInitializer_Int32Array()
{
return new int[3] { 1, 2, 3 };
}
#endif
#endregion
#region Object initializers

6
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -289,6 +289,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -289,6 +289,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
replacement.AcceptVisitor(this);
return;
}
if (TransformArrayInitializers.TransformRuntimeHelpersCreateSpanInitialization(inst, context, out var replacement2))
{
context.Step("TransformRuntimeHelpersCreateSpanInitialization: single-dim", inst);
inst.ReplaceWith(replacement2);
return;
}
base.VisitCall(inst);
TransformAssignment.HandleCompoundAssign(inst, context);
}

88
ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs

@ -119,28 +119,57 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -119,28 +119,57 @@ namespace ICSharpCode.Decompiler.IL.Transforms
replacement = null;
if (!context.Settings.ArrayInitializers)
return false;
if (MatchSpanTCtorWithPointerAndSize(inst, context, out var elementType, out var field, out var size))
{
if (field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA))
if (!MatchSpanTCtorWithPointerAndSize(inst, context, out var elementType, out var field, out var size))
return false;
if (!field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA))
return false;
var initialValue = field.GetInitialValue(context.PEFile, context.TypeSystem);
replacement = DecodeArrayInitializerOrUTF8StringLiteral(context, elementType, initialValue, size);
return replacement != null;
}
internal static bool TransformRuntimeHelpersCreateSpanInitialization(Call inst, StatementTransformContext context, out ILInstruction replacement)
{
var valuesList = new List<ILInstruction>();
replacement = null;
if (!context.Settings.ArrayInitializers)
return false;
if (!MatchRuntimeHelpersCreateSpan(inst, context, out var elementType, out var field))
return false;
if (!field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA))
return false;
if (IsSubPatternOfCpblkInitializer(inst))
return false;
var initialValue = field.GetInitialValue(context.PEFile, context.TypeSystem);
if (context.Settings.Utf8StringLiterals &&
elementType.IsKnownType(KnownTypeCode.Byte) &&
DecodeUTF8String(initialValue, size, out string text))
var elementTypeSize = elementType.GetSize();
if (elementTypeSize <= 0 || initialValue.Length % elementTypeSize != 0)
return false;
var size = initialValue.Length / elementTypeSize;
replacement = DecodeArrayInitializerOrUTF8StringLiteral(context, elementType, initialValue, size);
return replacement != null;
}
private static bool IsSubPatternOfCpblkInitializer(Call inst)
{
replacement = new LdStrUtf8(text);
return true;
if (inst.Parent is not AddressOf { Parent: Call { Parent: Cpblk cpblk } get_Item })
return false;
return MatchGetStaticFieldAddress(get_Item, out _);
}
private static ILInstruction DecodeArrayInitializerOrUTF8StringLiteral(StatementTransformContext context, IType elementType, BlobReader initialValue, int size)
{
if (context.Settings.Utf8StringLiterals && elementType.IsKnownType(KnownTypeCode.Byte)
&& DecodeUTF8String(initialValue, size, out string text))
{
return new LdStrUtf8(text);
}
var valuesList = new List<ILInstruction>();
if (DecodeArrayInitializer(elementType, initialValue, new[] { size }, valuesList))
{
var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.TypeSystem, elementType));
replacement = BlockFromInitializer(tempStore, elementType, new[] { size }, valuesList.ToArray());
return true;
}
return BlockFromInitializer(tempStore, elementType, new[] { size }, valuesList.ToArray());
}
}
return false;
return null;
}
private static unsafe bool DecodeUTF8String(BlobReader blob, int size, out string text)
@ -153,9 +182,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -153,9 +182,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
for (int i = 0; i < size; i++)
{
byte val = blob.CurrentPointer[i];
// If the string has control characters, it's probably binary data and not a string.
if (val < 0x20 && val is not ((byte)'\r' or (byte)'\n' or (byte)'\t'))
if (val == 0 && i == size - 1 && size > 1)
{
// Allow explicit null-termination character.
}
else if (val < 0x20 && val is not ((byte)'\r' or (byte)'\n' or (byte)'\t'))
{
// If the string has control characters, it's probably binary data and not a string.
text = null;
return false;
}
@ -187,6 +220,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -187,6 +220,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
static bool MatchRuntimeHelpersCreateSpan(Call inst, StatementTransformContext context, out IType elementType, out FieldDefinition field)
{
field = default;
elementType = null;
if (!IsRuntimeHelpers(inst.Method.DeclaringType))
return false;
if (inst.Arguments.Count != 1)
return false;
if (inst.Method is not { Name: "CreateSpan", TypeArguments: [var type] })
return false;
elementType = type;
if (!inst.Arguments[0].UnwrapConv(ConversionKind.StopGCTracking).MatchLdMemberToken(out var member))
return false;
if (member.MetadataToken.IsNil)
return false;
field = context.PEFile.Metadata.GetFieldDefinition((FieldDefinitionHandle)member.MetadataToken);
return true;
}
bool DoTransformMultiDim(ILFunction function, Block body, int pos)
{
if (pos >= body.Instructions.Count - 2)
@ -334,7 +386,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -334,7 +386,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
bool MatchGetStaticFieldAddress(ILInstruction input, out IField field)
static bool MatchGetStaticFieldAddress(ILInstruction input, out IField field)
{
if (input.MatchLdsFlda(out field))
return true;
@ -357,7 +409,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -357,7 +409,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return field != null;
}
static bool IsRuntimeHelpers(IType type) => type is { Name: "RuntimeHelpers", Namespace: "System.Runtime.CompilerServices" };
static bool IsRuntimeHelpers(IType type) => type is { Name: "RuntimeHelpers", Namespace: "System.Runtime.CompilerServices", TypeParameterCount: 0 };
unsafe bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store, ILInstruction locAllocInstruction, out IType elementType, out StObj[] values, out int instructionsToRemove)
{

Loading…
Cancel
Save