Skip to content

Replace RuntimeHelpers.CreateSpan<T>(LdMemberToken) with new T[] { } #3380

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ private static TypeC TestC()
#endif
#if CS110 && !NET40
public static ReadOnlySpan<byte> UTF8Literal => "Hello, world!"u8;
public static ReadOnlySpan<byte> UTF8LiteralWithNullTerminator => "Hello, world!\0"u8;
#endif
#endregion

Expand Down Expand Up @@ -805,6 +806,19 @@ private static void OutOfMemory()
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
Expand Down
6 changes: 6 additions & 0 deletions ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,12 @@ protected internal override void VisitCall(Call inst)
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);
}
Expand Down
100 changes: 76 additions & 24 deletions ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,28 +119,57 @@ internal static bool TransformSpanTArrayInitialization(NewObj inst, StatementTra
replacement = null;
if (!context.Settings.ArrayInitializers)
return false;
if (MatchSpanTCtorWithPointerAndSize(inst, context, out var elementType, out var field, out var size))
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)
{
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);
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)
{
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))
{
if (field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA))
{
var valuesList = new List<ILInstruction>();
var initialValue = field.GetInitialValue(context.PEFile, context.TypeSystem);
if (context.Settings.Utf8StringLiterals &&
elementType.IsKnownType(KnownTypeCode.Byte) &&
DecodeUTF8String(initialValue, size, out string text))
{
replacement = new LdStrUtf8(text);
return true;
}
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 new LdStrUtf8(text);
}
return false;
var valuesList = new List<ILInstruction>();
if (DecodeArrayInitializer(elementType, initialValue, new[] { size }, valuesList))
{
var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.TypeSystem, elementType));
return BlockFromInitializer(tempStore, elementType, new[] { size }, valuesList.ToArray());
}

return null;
}

private static unsafe bool DecodeUTF8String(BlobReader blob, int size, out string text)
Expand All @@ -153,9 +182,13 @@ private static unsafe bool DecodeUTF8String(BlobReader blob, int size, out strin
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;
}
Expand Down Expand Up @@ -187,6 +220,25 @@ static bool MatchSpanTCtorWithPointerAndSize(NewObj newObj, StatementTransformCo
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)
Expand Down Expand Up @@ -334,7 +386,7 @@ bool HandleCpblkInitializer(Block block, int pos, ILVariable v, long length, out
return true;
}

bool MatchGetStaticFieldAddress(ILInstruction input, out IField field)
static bool MatchGetStaticFieldAddress(ILInstruction input, out IField field)
{
if (input.MatchLdsFlda(out field))
return true;
Expand All @@ -357,7 +409,7 @@ bool MatchGetStaticFieldAddress(ILInstruction input, out IField field)
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)
{
Expand Down
Loading