Browse Source

Extend inline array pretty test to more receiver and access shapes

The existing fixture only exercised value-parameter and rvalue
receivers. Element reads and writes decompile correctly for every
lvalue receiver kind (instance/readonly/static fields, nested struct
fields, array elements, ref/in parameters, this inside the inline
array struct incl. readonly methods), for nested inline arrays,
reference-type elements, compound assignment/increment, and
ref/out/in element arguments, so pin all of that green.

Constructs whose output is correct but does not round-trip textually
are pinned via EXPECTED_OUTPUT: ref (readonly) locals to elements fold
into direct element access, index-from-end and System.Index indexing
print as constant/GetOffset indices, and range indexing prints as
Span casts plus Slice calls (Roslyn 4.14 lowers open-ended ranges
with an explicit length where newer compilers use the single-argument
Slice, hence the ROSLYN5 split). This replaces the TODO about
variable-length slicing: the output is compilable, just not sugared.

Foreach is pinned for the shapes that work: local, rvalue (via temp),
early break, and receiver mutation in the body. Broken foreach
receiver shapes are covered by the separate ignored InlineArrayForeach
fixture.

The fixture now uses System.Index/System.Range, which conflict with
ICSharpCode.Decompiler's public Index type when compiled into the
test assembly, so it moves to the ExcludedTestInputs list like
IndexRangeTest.

Assisted-by: Claude:claude-fable-5:Claude Code
tests/829-inline-arrays
Siegfried Pammer 2 months ago
parent
commit
5283f9b4da
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 315
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -227,6 +227,8 @@ @@ -227,6 +227,8 @@
<None Include="TestCases\ILPretty\WeirdEnums.cs" />
<Compile Remove="TestCases\Pretty\IndexRangeTest.cs" />
<None Include="TestCases\Pretty\IndexRangeTest.cs" />
<Compile Remove="TestCases\Pretty\InlineArrayTests.cs" />
<None Include="TestCases\Pretty\InlineArrayTests.cs" />
<Compile Remove="TestCases\Pretty\MetadataAttributes.cs" />
<None Include="TestCases\Pretty\MetadataAttributes.cs" />
<Compile Remove="TestCases\Pretty\NullableRefTypes.cs" />

315
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs

@ -17,6 +17,49 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -17,6 +17,49 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private T elem;
}
[InlineArray(4)]
public struct Nested4
{
private Byte16 elem;
}
[InlineArray(4)]
public struct String4
{
private string elem;
}
[InlineArray(8)]
public struct WithMethods
{
private int elem;
public int Get(int index)
{
return this[index];
}
public readonly int GetReadonly(int index)
{
return this[index];
}
public void Set(int index, int value)
{
this[index] = value;
}
}
public struct Wrapper
{
public Byte16 Buffer;
}
private Byte16 instanceBuffer;
private readonly Byte16 readonlyBuffer;
private static Byte16 staticBuffer;
private Wrapper wrapper;
public byte Byte0()
{
return GetByte16()[0];
@ -85,14 +128,194 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -85,14 +128,194 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
ReceiverReadOnlySpan(array[..8]);
}
// TODO
//public void Slice(Byte16 array, int end)
//{
// Receiver(array[..end]);
// Receiver((ReadOnlySpan<byte>)array[..end]);
// ReceiverSpan(array[..end]);
// ReceiverReadOnlySpan(array[..end]);
//}
public byte InstanceFieldRead()
{
return instanceBuffer[3];
}
public void InstanceFieldWrite(byte value)
{
instanceBuffer[3] = value;
}
public byte ReadOnlyFieldRead()
{
return readonlyBuffer[3];
}
public byte StaticFieldRead()
{
return staticBuffer[4];
}
public void StaticFieldWrite(byte value)
{
staticBuffer[4] = value;
}
public byte NestedFieldRead()
{
return wrapper.Buffer[2];
}
public void NestedFieldWrite(byte value)
{
wrapper.Buffer[2] = value;
}
public byte ArrayElementReceiverRead(Byte16[] array, int index)
{
return array[index][3];
}
public void ArrayElementReceiverWrite(Byte16[] array, int index, byte value)
{
array[index][3] = value;
}
public byte RefParamRead(ref Byte16 array)
{
return array[1];
}
public void RefParamWrite(ref Byte16 array, byte value)
{
array[1] = value;
}
public byte InParamRead(in Byte16 array)
{
return array[1];
}
public void CompoundAssign(ref Byte16 array, byte value)
{
array[2] += value;
array[3]++;
}
public void PassElementByRef(ref Byte16 array)
{
Mutate(ref array[5]);
}
public void PassElementByOut(ref Byte16 array)
{
Produce(out array[5]);
}
public void PassElementByIn(ref Byte16 array)
{
Consume(in array[5]);
}
public void RefLocalWrite(ref Byte16 array)
{
#if EXPECTED_OUTPUT
array[2] = 42;
#else
ref byte reference = ref array[2];
reference = 42;
#endif
}
public byte RefReadonlyLocalRead(in Byte16 array)
{
#if EXPECTED_OUTPUT
return array[6];
#else
ref readonly byte reference = ref array[6];
return reference;
#endif
}
public byte NestedRead(ref Nested4 array)
{
return array[1][2];
}
public void NestedWrite(ref Nested4 array, byte value)
{
array[1][2] = value;
}
public string StringRead(ref String4 array)
{
return array[0];
}
public void StringWrite(ref String4 array, string value)
{
array[0] = value;
}
public byte FromEndConst(Byte16 array)
{
#if EXPECTED_OUTPUT
return array[15];
#else
return array[^1];
#endif
}
public byte FromEndVariable(Byte16 array, Index index)
{
#if EXPECTED_OUTPUT
return array[index.GetOffset(16)];
#else
return array[index];
#endif
}
public Span<byte> RangeConst(ref Byte16 array)
{
#if EXPECTED_OUTPUT
return ((Span<byte>)array).Slice(1, 4);
#else
return array[1..5];
#endif
}
public Span<byte> RangeOpenEnd(ref Byte16 array)
{
#if EXPECTED_OUTPUT && ROSLYN5
return ((Span<byte>)array).Slice(2);
#elif EXPECTED_OUTPUT
return ((Span<byte>)array).Slice(2, 14);
#else
return array[2..];
#endif
}
public Span<byte> RangeFull(ref Byte16 array)
{
#if EXPECTED_OUTPUT
return array;
#else
return array[..];
#endif
}
public Span<byte> RangeVariableEnd(ref Byte16 array, int end)
{
#if EXPECTED_OUTPUT
return ((Span<byte>)array).Slice(0, end);
#else
return array[..end];
#endif
}
public Span<byte> RangeVariable(ref Byte16 array, Range range)
{
#if EXPECTED_OUTPUT
Range range2 = range;
int offset = range2.Start.GetOffset(16);
int length = range2.End.GetOffset(16) - offset;
return ((Span<byte>)array).Slice(offset, length);
#else
return array[range];
#endif
}
public byte VariableSplitting(Byte16 array, byte value)
{
@ -109,6 +332,68 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -109,6 +332,68 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return num;
}
public int SumForeachLocal()
{
Byte16 buffer = GetByte16();
int num = 0;
foreach (byte b in buffer)
{
num += b;
}
return num;
}
public int SumForeachRvalue()
{
#if EXPECTED_OUTPUT
int num = 0;
Byte16 buffer = GetByte16();
foreach (byte b in buffer)
{
num += b;
}
return num;
#else
int num = 0;
foreach (byte b in GetByte16())
{
num += b;
}
return num;
#endif
}
public int SumForeachBreak(Byte16 array, int limit)
{
int num = 0;
foreach (byte b in array)
{
if (num > limit)
{
break;
}
num += b;
}
return num;
}
public int SumForeachMutate(Byte16 array)
{
int num = 0;
foreach (byte b in array)
{
array[0] = b;
num += b;
}
return num;
}
public void SpanConversionFromField()
{
ReceiverSpan(instanceBuffer);
ReceiverReadOnlySpan(readonlyBuffer);
}
public void OverloadResolution()
{
Receiver(GetByte16());
@ -161,5 +446,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -161,5 +446,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public void ReceiverReadOnlySpan(ReadOnlySpan<byte> span)
{
}
public void Mutate(ref byte b)
{
b++;
}
public void Produce(out byte b)
{
b = 1;
}
public void Consume(in byte b)
{
}
}
}

Loading…
Cancel
Save