From f3c547a20552dd933aa97cbd3cc8cb677aac9e58 Mon Sep 17 00:00:00 2001 From: Adriano Carlos Verona Date: Fri, 29 Mar 2024 11:10:18 -0400 Subject: [PATCH] fixes handling of inline array when non literal indexes are used (#257) also fixes conversion to Span; generated method was calling generic methods (as opposed to their instantiations) --- .../Tests/Unit/DefaultExpressions.cs | 3 +- .../Tests/Unit/InlineArrayTests.cs | 33 +++++++++++++++++++ Cecilifier.Core/AST/InlineArrayProcessor.cs | 2 +- .../PrivateImplementationDetails.Generator.cs | 31 +++++++++++++---- 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/Cecilifier.Core.Tests/Tests/Unit/DefaultExpressions.cs b/Cecilifier.Core.Tests/Tests/Unit/DefaultExpressions.cs index c6d3ade4..575542e7 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/DefaultExpressions.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/DefaultExpressions.cs @@ -132,9 +132,10 @@ public void TestDefaultLiteralExpression(string type, string expected) public void TestDefaultLiteralExpressionWithStructs(string code) { var expected = """ - \s+//.+ v = default; + \s+//(?:.+ v = default)|(?:Foo v); \s+var (l_v_\d+) = new VariableDefinition\((.+)\); \s+m_topLevelStatements_\d+.Body.Variables.Add\(\1\); + (?:\s+//v = default;)? \s+(il_topLevelMain_\d+\.Emit\(OpCodes\.)Ldloca, l_v_\d+\); \s+\3Initobj, \2\); """; diff --git a/Cecilifier.Core.Tests/Tests/Unit/InlineArrayTests.cs b/Cecilifier.Core.Tests/Tests/Unit/InlineArrayTests.cs index 575117bf..4ae6055b 100644 --- a/Cecilifier.Core.Tests/Tests/Unit/InlineArrayTests.cs +++ b/Cecilifier.Core.Tests/Tests/Unit/InlineArrayTests.cs @@ -50,6 +50,9 @@ public struct IntBuffer var cecilifiedCode = result.GeneratedCode.ReadToEnd(); Assert.That(cecilifiedCode, Does.Match("""new TypeDefinition\("", "", .+\)""")); + Assert.That(cecilifiedCode, Does.Match("""cls_privateImplementationDetails_\d+.Methods.Add\(m_inlineArrayAsSpan_\d+\);""")); + Assert.That(cecilifiedCode, Does.Match("""(m_inlineArrayAsSpan_\d+)_inst.Add\(_ = \1_il.Create\(OpCodes.Call, gi_unsafeAs_\d+\)\);""")); + Assert.That(cecilifiedCode, Does.Match("""(m_inlineArrayAsSpan_\d+)_inst.Add\(_ = \1_il.Create\(OpCodes.Call, gi_createSpan_\d+\)\);""")); } [Test] @@ -231,6 +234,36 @@ public struct Buffer { private {{elementType}} _element0; } Assert.That(cecilified, Does.Match(expectedIL)); } + + [Test] + public void AccessToIndex_ThroughNonLiteral_UsesElementRefMethodAndIndex() + { + var result = RunCecilifier(""" + class C + { + int M(int i) + { + Buffer b = new Buffer(); + return b[i]; + } + } + + [System.Runtime.CompilerServices.InlineArray(5)] + public struct Buffer { private int _element0; } + """); + + var cecilified = result.GeneratedCode.ReadToEnd(); + Assert.That(cecilified, Does.Match(""" + \s+//return b\[i\]; + (\s+il_M_5.Emit\(OpCodes\.)Ldloca, l_b_7\); + \1Ldarg_1\); + """)); + + Assert.That(cecilified, Does.Match(""" + (\s+il_M_\d+\.Emit\(OpCodes\.)Call, gi_inlineArrayElementRef_\d+\); + \1Ldind_I4\); + """)); + } [Test] public void InlineArray_MemberAccess_OnIndex() diff --git a/Cecilifier.Core/AST/InlineArrayProcessor.cs b/Cecilifier.Core/AST/InlineArrayProcessor.cs index 5f107b26..4597768b 100644 --- a/Cecilifier.Core/AST/InlineArrayProcessor.cs +++ b/Cecilifier.Core/AST/InlineArrayProcessor.cs @@ -97,7 +97,7 @@ internal static bool TryHandleIntIndexElementAccess(IVisitorContext context, str } else { - context.EmitCilInstruction(ilVar, OpCodes.Ldc_I4, index); + ExpressionVisitor.Visit(context, ilVar, elementAccess.ArgumentList.Arguments[0].Expression); method = InlineArrayElementRefMethodFor(context, inlineArrayType); } context.EmitCilInstruction(ilVar, OpCodes.Call, method); diff --git a/Cecilifier.Core/CodeGeneration/PrivateImplementationDetails.Generator.cs b/Cecilifier.Core/CodeGeneration/PrivateImplementationDetails.Generator.cs index 625fcd0c..7276d951 100644 --- a/Cecilifier.Core/CodeGeneration/PrivateImplementationDetails.Generator.cs +++ b/Cecilifier.Core/CodeGeneration/PrivateImplementationDetails.Generator.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Mono.Cecil; using Mono.Cecil.Cil; namespace Cecilifier.Core.CodeGeneration; @@ -71,21 +72,37 @@ internal static string GetOrEmmitInlineArrayAsSpanMethod(IVisitorContext context return context.TypeResolver.Resolve(context.RoslynTypeSystem.SystemSpan).MakeGenericInstanceType(spanTypeParameter); }); + var tBufferVar = ResolveOwnedGenericParameter(context, "TBuffer", methodTypeQualifiedName); + var tElementVar = ResolveOwnedGenericParameter(context, "TElement", methodTypeQualifiedName); + + var unsafeAsVar = context.Naming.SyntheticVariable("unsafeAs", ElementKind.GenericInstance); + var unsafeAsExps = GetUnsafeAsMethod(context).MethodResolverExpression(context).MakeGenericInstanceMethod(unsafeAsVar, [tBufferVar, tElementVar]); + + var memoryMarshalCreateSpanVar = context.Naming.SyntheticVariable("createSpan", ElementKind.GenericInstance); + var memoryMarshalCreateSpanExps = GetMemoryMarshalCreateSpanMethod(context).MethodResolverExpression(context).MakeGenericInstanceMethod(memoryMarshalCreateSpanVar, [tElementVar]); + var methodBodyExpressions = CecilDefinitionsFactory.MethodBody( methodVar, [ OpCodes.Ldarg_0, - OpCodes.Call.WithOperand(GetUnsafeAsMethod(context).MethodResolverExpression(context)), + OpCodes.Call.WithOperand(unsafeAsVar), OpCodes.Ldarg_1, - OpCodes.Call.WithOperand(GetMemoryMarshalCreateSpanMethod(context).MethodResolverExpression(context)), + OpCodes.Call.WithOperand(memoryMarshalCreateSpanVar), OpCodes.Ret ]); + + var finalExps = methodExpressions + .Append("") + .Append("// Unsafe.As() generic instance method") + .Concat(unsafeAsExps) + .Append("") + .Append("// MemoryMarshal.CreateSpan() generic instance method") + .Concat(memoryMarshalCreateSpanExps) + .Append("") + .Concat(methodBodyExpressions) + .Append($"{privateImplementationDetailsVar.VariableName}.Methods.Add({methodVar});"); - foreach (var exp in methodExpressions.Concat(methodBodyExpressions)) - { - context.WriteCecilExpression(exp); - context.WriteNewLine(); - } + context.WriteCecilExpressions(finalExps); return methodVar;