mirror of https://github.com/icsharpcode/ILSpy.git
14 changed files with 625 additions and 2 deletions
@ -0,0 +1,138 @@ |
|||||||
|
using System; |
||||||
|
using System.Runtime.CompilerServices; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty |
||||||
|
{ |
||||||
|
public class InlineArrayTests |
||||||
|
{ |
||||||
|
[InlineArray(16)] |
||||||
|
public struct Byte16 |
||||||
|
{ |
||||||
|
private byte elem; |
||||||
|
} |
||||||
|
|
||||||
|
[InlineArray(16)] |
||||||
|
public struct Generic16<T> |
||||||
|
{ |
||||||
|
private T elem; |
||||||
|
} |
||||||
|
|
||||||
|
public byte Byte0() |
||||||
|
{ |
||||||
|
return GetByte16()[0]; |
||||||
|
} |
||||||
|
|
||||||
|
public byte GenericByte0() |
||||||
|
{ |
||||||
|
return GetGeneric<byte>()[0]; |
||||||
|
} |
||||||
|
|
||||||
|
public byte Byte5() |
||||||
|
{ |
||||||
|
return GetByte16()[5]; |
||||||
|
} |
||||||
|
|
||||||
|
public byte GenericByte5() |
||||||
|
{ |
||||||
|
return GetGeneric<byte>()[5]; |
||||||
|
} |
||||||
|
|
||||||
|
public byte ByteN() |
||||||
|
{ |
||||||
|
return GetByte16()[GetIndex()]; |
||||||
|
} |
||||||
|
|
||||||
|
public byte GenericByteN() |
||||||
|
{ |
||||||
|
return GetGeneric<byte>()[GetIndex()]; |
||||||
|
} |
||||||
|
|
||||||
|
public byte Byte0(Byte16 array, byte value) |
||||||
|
{ |
||||||
|
return array[0] = value; |
||||||
|
} |
||||||
|
|
||||||
|
public byte GenericByte0(Generic16<byte> array, byte value) |
||||||
|
{ |
||||||
|
return array[0] = value; |
||||||
|
} |
||||||
|
|
||||||
|
public byte Byte5(Byte16 array, byte value) |
||||||
|
{ |
||||||
|
return array[5] = value; |
||||||
|
} |
||||||
|
|
||||||
|
public byte GenericByte5(Generic16<byte> array, byte value) |
||||||
|
{ |
||||||
|
return array[5] = value; |
||||||
|
} |
||||||
|
|
||||||
|
public byte ByteN(Byte16 array, byte value) |
||||||
|
{ |
||||||
|
return array[GetIndex()] = value; |
||||||
|
} |
||||||
|
|
||||||
|
public byte GenericByteN(Generic16<byte> array, byte value) |
||||||
|
{ |
||||||
|
return array[GetIndex()] = value; |
||||||
|
} |
||||||
|
|
||||||
|
public byte VariableSplitting(Byte16 array, byte value) |
||||||
|
{ |
||||||
|
return array[GetIndex()] = (array[GetIndex() + 1] = value); |
||||||
|
} |
||||||
|
|
||||||
|
public void OverloadResolution() |
||||||
|
{ |
||||||
|
Receiver(GetByte16()); |
||||||
|
Receiver((object)GetByte16()); |
||||||
|
Byte16 buffer = GetByte16(); |
||||||
|
Receiver((Span<byte>)buffer); |
||||||
|
Byte16 buffer2 = GetByte16(); |
||||||
|
Receiver((ReadOnlySpan<byte>)buffer2); |
||||||
|
Byte16 buffer3 = GetByte16(); |
||||||
|
ReceiverSpan((Span<byte>)buffer3); |
||||||
|
Byte16 buffer4 = GetByte16(); |
||||||
|
ReceiverReadOnlySpan((ReadOnlySpan<byte>)buffer4); |
||||||
|
} |
||||||
|
|
||||||
|
public Byte16 GetByte16() |
||||||
|
{ |
||||||
|
return default(Byte16); |
||||||
|
} |
||||||
|
|
||||||
|
public Generic16<T> GetGeneric<T>() |
||||||
|
{ |
||||||
|
return default(Generic16<T>); |
||||||
|
} |
||||||
|
|
||||||
|
public int GetIndex() |
||||||
|
{ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
public void Receiver(Span<byte> span) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public void Receiver(ReadOnlySpan<byte> span) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public void Receiver(Byte16 span) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public void Receiver(object span) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public void ReceiverSpan(Span<byte> span) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public void ReceiverReadOnlySpan(ReadOnlySpan<byte> span) |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,234 @@ |
|||||||
|
// Copyright (c) 2025 Siegfried Pammer
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
#nullable enable |
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis; |
||||||
|
|
||||||
|
using ICSharpCode.Decompiler.TypeSystem; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.IL.Transforms |
||||||
|
{ |
||||||
|
static class InlineArrayTransform |
||||||
|
{ |
||||||
|
internal static bool RunOnExpression(Call inst, StatementTransformContext context) |
||||||
|
{ |
||||||
|
if (MatchSpanIndexerWithInlineArrayAsSpan(inst, out var type, out var addr, out var index, out bool isReadOnly)) |
||||||
|
{ |
||||||
|
if (isReadOnly) |
||||||
|
{ |
||||||
|
context.Step("call get_Item(addressof System.ReadOnlySpan{T}(call InlineArrayAsReadOnlySpan(addr)), index) -> readonly.ldelema.inlinearray(addr, index)", inst); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
context.Step("call get_Item(addressof System.Span{T}(call InlineArrayAsSpan(addr)), index) -> ldelema.inlinearray(addr, index)", inst); |
||||||
|
} |
||||||
|
inst.ReplaceWith(new LdElemaInlineArray(type, addr, index) { IsReadOnly = isReadOnly }.WithILRange(inst)); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (MatchInlineArrayElementRef(inst, out type, out addr, out index, out isReadOnly)) |
||||||
|
{ |
||||||
|
if (isReadOnly) |
||||||
|
{ |
||||||
|
context.Step("call InlineArrayElementRefReadOnly(addr, index) -> readonly.ldelema.inlinearray(addr, index)", inst); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
context.Step("call InlineArrayElementRef(addr, index) -> ldelema.inlinearray(addr, index)", inst); |
||||||
|
} |
||||||
|
inst.ReplaceWith(new LdElemaInlineArray(type, addr, index) { IsReadOnly = isReadOnly }.WithILRange(inst)); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (MatchInlineArrayFirstElementRef(inst, out type, out addr, out isReadOnly)) |
||||||
|
{ |
||||||
|
if (isReadOnly) |
||||||
|
{ |
||||||
|
context.Step("call InlineArrayFirstElementRefReadOnly(addr) -> readonly.ldelema.inlinearray(addr, ldc.i4 0)", inst); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
context.Step("call InlineArrayFirstElementRef(addr) -> ldelema.inlinearray(addr, ldc.i4 0)", inst); |
||||||
|
} |
||||||
|
inst.ReplaceWith(new LdElemaInlineArray(type, addr, new LdcI4(0)) { IsReadOnly = isReadOnly }.WithILRange(inst)); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Matches call get_Item(addressof System.(ReadOnly)Span[[T]](call InlineArrayAs(ReadOnly)Span(addr, length)), index)
|
||||||
|
/// </summary>
|
||||||
|
static bool MatchSpanIndexerWithInlineArrayAsSpan(Call inst, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? addr, [NotNullWhen(true)] out ILInstruction? index, out bool isReadOnly) |
||||||
|
{ |
||||||
|
isReadOnly = false; |
||||||
|
type = null; |
||||||
|
addr = null; |
||||||
|
index = null; |
||||||
|
|
||||||
|
if (MatchSpanGetItem(inst.Method, "ReadOnlySpan")) |
||||||
|
{ |
||||||
|
isReadOnly = true; |
||||||
|
|
||||||
|
if (inst.Arguments is not [AddressOf { Value: Call targetInst, Type: var typeInfo }, var indexInst]) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (!MatchInlineArrayHelper(targetInst.Method, "InlineArrayAsReadOnlySpan", out var inlineArrayType)) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (targetInst.Arguments is not [var addrInst, LdcI4]) |
||||||
|
return false; |
||||||
|
|
||||||
|
type = inlineArrayType; |
||||||
|
addr = addrInst; |
||||||
|
index = indexInst; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
else if (MatchSpanGetItem(inst.Method, "Span")) |
||||||
|
{ |
||||||
|
if (inst.Arguments is not [AddressOf { Value: Call targetInst, Type: var typeInfo }, var indexInst]) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (!MatchInlineArrayHelper(targetInst.Method, "InlineArrayAsSpan", out var inlineArrayType)) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (targetInst.Arguments is not [var addrInst, LdcI4]) |
||||||
|
return false; |
||||||
|
|
||||||
|
type = inlineArrayType; |
||||||
|
addr = addrInst; |
||||||
|
index = indexInst; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Matches call InlineArrayElementRef(ReadOnly)(addr, index)
|
||||||
|
/// </summary>
|
||||||
|
static bool MatchInlineArrayElementRef(Call inst, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? addr, [NotNullWhen(true)] out ILInstruction? index, out bool isReadOnly) |
||||||
|
{ |
||||||
|
type = null; |
||||||
|
addr = null; |
||||||
|
index = null; |
||||||
|
isReadOnly = false; |
||||||
|
|
||||||
|
if (inst.Arguments is not [var addrInst, var indexInst]) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (MatchInlineArrayHelper(inst.Method, "InlineArrayElementRef", out var inlineArrayType)) |
||||||
|
{ |
||||||
|
isReadOnly = false; |
||||||
|
type = inlineArrayType; |
||||||
|
addr = addrInst; |
||||||
|
index = indexInst; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (MatchInlineArrayHelper(inst.Method, "InlineArrayElementRefReadOnly", out inlineArrayType)) |
||||||
|
{ |
||||||
|
isReadOnly = true; |
||||||
|
type = inlineArrayType; |
||||||
|
addr = addrInst; |
||||||
|
index = indexInst; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private static bool MatchInlineArrayFirstElementRef(Call inst, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? addr, out bool isReadOnly) |
||||||
|
{ |
||||||
|
type = null; |
||||||
|
addr = null; |
||||||
|
isReadOnly = false; |
||||||
|
|
||||||
|
if (inst.Arguments is not [var addrInst]) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (MatchInlineArrayHelper(inst.Method, "InlineArrayFirstElementRef", out var inlineArrayType)) |
||||||
|
{ |
||||||
|
isReadOnly = false; |
||||||
|
type = inlineArrayType; |
||||||
|
addr = addrInst; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (MatchInlineArrayHelper(inst.Method, "InlineArrayFirstElementRefReadOnly", out inlineArrayType)) |
||||||
|
{ |
||||||
|
isReadOnly = true; |
||||||
|
type = inlineArrayType; |
||||||
|
addr = addrInst; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
static bool MatchSpanGetItem(IMethod method, string typeName) |
||||||
|
{ |
||||||
|
return method is { |
||||||
|
IsStatic: false, |
||||||
|
Name: "get_Item", |
||||||
|
DeclaringType: { Namespace: "System", Name: string name, TypeParameterCount: 1, DeclaringType: null } |
||||||
|
} && typeName == name; |
||||||
|
} |
||||||
|
|
||||||
|
static bool MatchInlineArrayHelper(IMethod method, string methodName, [NotNullWhen(true)] out IType? inlineArrayType) |
||||||
|
{ |
||||||
|
inlineArrayType = null; |
||||||
|
if (method is not { |
||||||
|
IsStatic: true, Name: var name, |
||||||
|
DeclaringType: { FullName: "<PrivateImplementationDetails>", TypeParameterCount: 0 }, |
||||||
|
TypeArguments: [var bufferType, _], |
||||||
|
Parameters: var parameters |
||||||
|
}) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (methodName != name) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (methodName.Contains("FirstElement")) |
||||||
|
{ |
||||||
|
if (parameters is not [{ Type: ByReferenceType { ElementType: var type } }]) |
||||||
|
return false; |
||||||
|
if (!type.Equals(bufferType)) |
||||||
|
return false; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (parameters is not [{ Type: ByReferenceType { ElementType: var type } }, { Type: var lengthOrIndexParameterType }]) |
||||||
|
return false; |
||||||
|
if (!type.Equals(bufferType) || !lengthOrIndexParameterType.IsKnownType(KnownTypeCode.Int32)) |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
inlineArrayType = bufferType; |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue