mirror of https://github.com/icsharpcode/ILSpy.git
14 changed files with 625 additions and 2 deletions
@ -0,0 +1,138 @@
@@ -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 @@
@@ -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