From 101ddf878396229d789ccb7b456c62a6fee69aa0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 5 Jun 2025 21:57:53 +0200 Subject: [PATCH] Add support for constant slices of InlineArrays --- .../TestCases/Pretty/InlineArrayTests.cs | 17 +++++++++++++++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 21 ++++++++++++++++++- .../CSharp/ExpressionBuilder.cs | 7 +------ .../CSharp/Resolver/CSharpConversions.cs | 4 ++-- .../TypeSystem/TypeSystemExtensions.cs | 16 ++++++++++++++ 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs index 886947d31..ea7a74338 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs @@ -77,6 +77,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return array[GetIndex()] = value; } + public void Slice(Byte16 array) + { + Receiver(array[..8]); + Receiver((ReadOnlySpan)array[..8]); + ReceiverSpan(array[..8]); + ReceiverReadOnlySpan(array[..8]); + } + + // TODO + //public void Slice(Byte16 array, int end) + //{ + // Receiver(array[..end]); + // Receiver((ReadOnlySpan)array[..end]); + // ReceiverSpan(array[..end]); + // ReceiverReadOnlySpan(array[..end]); + //} + public byte VariableSplitting(Byte16 array, byte value) { return array[GetIndex()] = (array[GetIndex() + 1] = value); diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 298adf7c4..058eff4e3 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -468,16 +468,35 @@ namespace ICSharpCode.Decompiler.CSharp && argumentList.Length == 2) { argumentList.CheckNoNamedOrOptionalArguments(); + var arrayType = method.TypeArguments[0]; + var arrayLength = arrayType.GetInlineArrayLength(); + var arrayElementType = arrayType.GetInlineArrayElementType(); var argument = argumentList.Arguments[0]; + var spanLengthExpr = argumentList.Arguments[1]; var targetType = method.ReturnType; + var spanType = typeSystem.FindType(KnownTypeCode.SpanOfT); if (argument.Expression is DirectionExpression { FieldDirection: FieldDirection.In or FieldDirection.Ref, Expression: var lvalueExpr }) { // `(TargetType)(in arg)` is invalid syntax. // Also, `f(in arg)` is invalid when there's an implicit conversion involved. argument = argument.UnwrapChild(lvalueExpr); } - return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression) + if (spanLengthExpr.ResolveResult.ConstantValue is int spanLength && spanLength <= arrayLength) + { + if (spanLength < arrayLength) + { + argument = new IndexerExpression(argument.Expression, new BinaryOperatorExpression { + Operator = BinaryOperatorType.Range, + Right = spanLengthExpr.Expression + }).WithRR(new ResolveResult(new ParameterizedType(spanType, arrayElementType))).WithoutILInstruction(); + if (targetType.IsKnownType(KnownTypeCode.SpanOfT)) + { + return argument; + } + } + return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression) .WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, Conversion.InlineArrayConversion)); + } } if (settings.LiftNullables && method.Name == "GetValueOrDefault" diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 43fd7bb78..4fc23581c 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -3138,18 +3138,13 @@ namespace ICSharpCode.Decompiler.CSharp memberStatic: false, memberDeclaringType: inst.Type ); - var inlineArrayElementType = GetInlineArrayElementType(inst.Type); + var inlineArrayElementType = inst.Type.GetInlineArrayElementType(); IndexerExpression indexerExpr = new IndexerExpression( arrayExpr, inst.Indices.Select(i => TranslateArrayIndex(i).Expression) ); TranslatedExpression expr = indexerExpr.WithILInstruction(inst).WithRR(new ResolveResult(inlineArrayElementType)); return new DirectionExpression(FieldDirection.Ref, expr) .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); - - IType GetInlineArrayElementType(IType arrayType) - { - return arrayType?.GetFields(f => !f.IsStatic).SingleOrDefault()?.Type ?? SpecialType.UnknownType; - } } TranslatedExpression TranslateArrayIndex(ILInstruction i) diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs index 0d1789ea7..6acbeaa49 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs @@ -233,9 +233,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver if ((toType.IsKnownType(KnownTypeCode.SpanOfT) || toType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) && fromType.IsInlineArrayType()) { - var @field = fromType.GetFields(f => !f.IsStatic, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); + var elementType = fromType.GetInlineArrayElementType(); var spanElementType = toType.TypeArguments[0]; - if (field != null && IdentityConversion(field.ReturnType, spanElementType)) + if (IdentityConversion(elementType, spanElementType)) return Conversion.InlineArrayConversion; } return Conversion.None; diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index 54cfaf9a4..6b9dcb352 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -316,6 +316,22 @@ namespace ICSharpCode.Decompiler.TypeSystem return td.HasAttribute(KnownAttribute.InlineArray); } + public static int? GetInlineArrayLength(this IType type) + { + if (type.Kind != TypeKind.Struct) + return null; + var td = type.GetDefinition(); + if (td == null) + return null; + var attr = td.GetAttribute(KnownAttribute.InlineArray); + return attr?.FixedArguments.FirstOrDefault().Value as int?; + } + + public static IType GetInlineArrayElementType(this IType arrayType) + { + return arrayType?.GetFields(f => !f.IsStatic).SingleOrDefault()?.Type ?? SpecialType.UnknownType; + } + /// /// Gets whether the type is the specified known type. /// For generic known types, this returns true for any parameterization of the type (and also for the definition itself).