Skip to content

Commit

Permalink
Merge pull request #329 from adrianoc/staging
Browse files Browse the repository at this point in the history
February 2025 Update 1 (Version 2.18)
  • Loading branch information
adrianoc authored Feb 5, 2025
2 parents acd1333 + 01f9120 commit 08fd73f
Show file tree
Hide file tree
Showing 43 changed files with 687 additions and 310 deletions.
2 changes: 1 addition & 1 deletion Cecilifier.Common.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>12</LangVersion>
<AssemblyVersion>2.17.0</AssemblyVersion>
<AssemblyVersion>2.18.0</AssemblyVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
7 changes: 0 additions & 7 deletions Cecilifier.Core.Tests/Tests/Integration/CodeBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,6 @@ public void NestedIfStatementTest()
AssertResourceTestWithExplicitExpectation(@"CodeBlock/Conditional/NestedIfStatement", "System.Void NestedIfStatement::Foo(System.Int32)");
}

[Test]
[Ignore("Not Implemented yet")]
public void NullCoalescingTest()
{
AssertResourceTest(@"CodeBlock/Conditional/");
}

[Test]
[Ignore("Not Implemented yet")]
public void SwitchStatementTest()
Expand Down
4 changes: 2 additions & 2 deletions Cecilifier.Core.Tests/Tests/Integration/GenericsTestCase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ public void TestInstanceNonGenericMethodsOnGenericTypes()
[Test]
public void TestGenericInferredStaticMethods()
{
AssertResourceTest(@"Generics/StaticInferredMethods");
AssertResourceTest("Generics/StaticInferredMethods");
}

[Test]
public void TestGenericExplicitStaticMethods()
{
AssertResourceTest(@"Generics/StaticExplicitMethods");
AssertResourceTest("Generics/StaticExplicitMethods");
}

[Test]
Expand Down
19 changes: 19 additions & 0 deletions Cecilifier.Core.Tests/Tests/OutputBased/LocalFunctionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Cecilifier.Core.Tests.Framework;
using NUnit.Framework;

namespace Cecilifier.Core.Tests.OutputBased;

[TestFixture]
public class LocalFunctionTests : OutputBasedTestBase
{
[TestCase("static", TestName = "Static")]
[TestCase("", TestName = "Instance")]
public void InstanceLocalFunction(string staticOrInstance)
{
AssertOutput($"""
System.Console.Write(M(1));
{staticOrInstance} int M(int i) => 41 + i;
""",
"42");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Cecilifier.Core.Tests.Framework;
using NUnit.Framework;

namespace Cecilifier.Core.Tests.OutputBased;

[TestFixture]
public class NullCoalescingOperatorTests : OutputBasedTestBase
{
[Test]
public void SimpleNullableValueType()
{
AssertOutput("""
System.Console.Write(M(42, 1));
int? M(int? i1, int? i2) => i1 ?? i2;
""",
"42");
}

[Test]
public void SimpleReferenceType()
{
AssertOutput("""
System.Console.Write(M(null, "42"));
object M(object o1, object o2) => o1 ?? o2;
""",
"42");
}

[Test]
public void MixedNullableValueType_AndReferenceType()
{
AssertOutput("""
var r = M3(42, 1);
System.Console.Write(r.Value);
int? M3(int? i1, object i2) => i1 ?? (int) i2;
""",
"42");
}

[TestCase("(int?) o1 ?? (int?) o2")]
[TestCase("(int?) o1 ?? (int) o2")]
public void Convoluted(string coalescingExpression)
{
AssertOutput($"""
var r = M(42, 1);
System.Console.Write(r.Value);
int? M(object o1, object o2) => {coalescingExpression};
""",
"42");
}

[TestCase(null, null, "C", "C")]
[TestCase(null, "B", null, "B")]
[TestCase(null, "B", "C", "B")]
[TestCase("A", "B", "C", "A")]
[TestCase("A", "B", null, "A")]
public void TestAssociative(string a, string b, string c, string expectedOutput)
{
AssertOutput($"System.Console.Write({Quote(a)} ?? {Quote(b)} ?? {Quote(c)});", expectedOutput);

string? Quote(string s) => s == null ? "null" : $"\"{s}\"";
}
}
4 changes: 2 additions & 2 deletions Cecilifier.Core.Tests/Tests/Unit/ArrayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public void MemberAccessOnElementAccessOnValueTypeArray_LoadsElementAddress(stri
{
var result = RunCecilifier($$"""int M(S[] sa) => sa[0].{{member}}; struct S { public int Property { get; set; } public int Field; public int Method() => 1; }""");
Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match($"""
(il_M_\d+\.Emit\(OpCodes\.)Ldarg_1\);
(il_M_\d+\.Emit\(OpCodes\.)Ldarg_0\);
\s+\1Ldc_I4, 0\);
\s+\1Ldelema, st_S_0\);
\s+\1{expectedILMemberRef}\);
Expand All @@ -187,7 +187,7 @@ public void MemberAccessOnElementAccessOnReferenceTypeArray_LoadsElementByRefere
{
var result = RunCecilifier($$"""int M(S[] sa) => sa[0].{{member}}; class S { public int Property { get; set; } public int Field; public int Method() => 1; }""");
Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match($"""
(il_M_\d+\.Emit\(OpCodes\.)Ldarg_1\);
(il_M_\d+\.Emit\(OpCodes\.)Ldarg_0\);
\s+\1Ldc_I4, 0\);
\s+\1Ldelem_Ref\);
\s+\1{expectedILMemberRef}\);
Expand Down
94 changes: 94 additions & 0 deletions Cecilifier.Core.Tests/Tests/Unit/AttributesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,100 @@ class Foo<TFoo> {{ }}");
$@"(?s)var (attr_myGeneric_1_\d+) = new CustomAttribute\(new MethodReference\((ctor_myGenericAttribute_\d+)\.Name.+\2\.ReturnType\).+DeclaringType = cls_myGenericAttribute_\d+.MakeGenericInstanceType\(.*{expectedType}\).+\);\s+" +
@"cls_foo_\d+\.CustomAttributes\.Add\(\1\);"));
}

[Test]
public void ForwardReferenceToGenericAttributeWorks()
{
var result = RunCecilifier($$"""
[MyGeneric<int>]
class Foo<TFoo> { }
{{GenericAttributeDefinition}}
""");

var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(
cecilifiedCode,
Does.Match("""
\s+assembly\.MainModule\.Types\.Add\((?<targetType>cls_foo_\d+)\);
\s+var (?<attr>attr_myGeneric_\d+_\d+) = new CustomAttribute\(.+(?<attrCtor>ctor_myGenericAttribute_\d+).Name, \k<attrCtor>.ReturnType\) {.+DeclaringType = cls_myGenericAttribute_\d+.MakeGenericInstanceType\(assembly.MainModule.TypeSystem.Int32\), .+\);
\s+cls_foo_22.CustomAttributes.Add\(\k<attr>\);
"""));
}

[Test]
public void ForwardReferenceToGenericAttributeWorks2()
{
var result = RunCecilifier($$"""
class Foo
{
[MyGeneric<int>]
void M() {}
}
{{GenericAttributeDefinition}}
""");

var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(
cecilifiedCode,
Does.Match("""
\s+var (?<m>m_M_\d+) = new MethodDefinition\("M",.+\);
\s+cls_foo_\d+.Methods.Add\(\k<m>\);
\s+var (?<attr>attr_myGeneric_\d+_\d+) = new CustomAttribute\(.+(?<attrCtor>ctor_myGenericAttribute_\d+).Name, \k<attrCtor>.ReturnType\) {.+DeclaringType = cls_myGenericAttribute_\d+.MakeGenericInstanceType\(assembly.MainModule.TypeSystem.Int32\), .+\);
\s+\k<m>.CustomAttributes.Add\(\k<attr>\);
"""));
}

[Test]
public void CyclicForwardReferenceToGenericAttributeWorks1()
{
var result = RunCecilifier("""
[MyGeneric<Foo>]
class Foo { }
class MyGenericAttribute<T> : System.Attribute {}
""");

var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(
cecilifiedCode,
Does.Match("""
\s+assembly\.MainModule\.Types\.Add\((?<targetType>cls_foo_\d+)\);
\s+var (?<attr>attr_myGeneric_\d+_\d+) = new CustomAttribute\(.+(?<attrCtor>ctor_myGenericAttribute_\d+).Name, \k<attrCtor>.ReturnType\) {.+DeclaringType = cls_myGenericAttribute_\d+.MakeGenericInstanceType\(\k<targetType>\), .+\);
\s+cls_foo_\d+.CustomAttributes.Add\(\k<attr>\);
"""));
}

[Test]
public void CyclicForwardReferenceToGenericAttributeWorks2()
{
var result = RunCecilifier("""
[MyGeneric<int>]
class Foo { }
class MyGenericAttribute<T> : System.Attribute
{
public Foo _foo;
}
""");

var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(
cecilifiedCode,
Does.Match("""
//Class : Foo
\s+var (?<appliedTo>cls_foo_\d+) = new TypeDefinition\("", "Foo",.+\);
\s+assembly.MainModule.Types.Add\(\k<appliedTo>\);
\s+//Class : MyGenericAttribute
\s+var (?<attrType>cls_myGenericAttribute_\d+) = new TypeDefinition\("", "MyGenericAttribute`1",.+ImportReference\(typeof\(System.Attribute\)\)\);
\s+var gp_T_\d+ = new Mono.Cecil.GenericParameter\("T", \k<attrType>\);
\s+\k<attrType>.GenericParameters.Add\(gp_T_\d+\);
\s+assembly.MainModule.Types.Add\(\k<attrType>\);
\s+var ctor_myGenericAttribute_4 = new MethodDefinition\(".ctor",.+TypeSystem.Void\);
\s+var (?<attrInstance>attr_myGeneric_1_\d+) = new CustomAttribute\(new MethodReference\(ctor_myGenericAttribute_4.Name, ctor_myGenericAttribute_4.ReturnType\) {.+DeclaringType = \k<attrType>.MakeGenericInstanceType\(.+Int32\).+}\);
\s+\k<appliedTo>.CustomAttributes.Add\(\k<attrInstance>\);
"""));
}

[TestCase("LayoutKind.Auto, Pack=1, Size=12", 1, 12)]
[TestCase("LayoutKind.Auto, Pack=1", 1, 0)]
Expand Down
4 changes: 2 additions & 2 deletions Cecilifier.Core.Tests/Tests/Unit/CastTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public void Unbox()
{
var result = RunCecilifier("int UnboxIt(object o) => (int) o;");
Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match("""
(il_unboxIt_\d+\.Emit\(OpCodes\.)Ldarg_1\);
(il_unboxIt_\d+\.Emit\(OpCodes\.)Ldarg_0\);
\s+\1Unbox_Any, assembly.MainModule.TypeSystem.Int32\);
"""));
}
Expand All @@ -22,7 +22,7 @@ public void Box(string expression)
{
var result = RunCecilifier($"object BoxIt(int i) => {expression};");
Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match("""
(il_boxIt_\d+\.Emit\(OpCodes\.)Ldarg_1\);
(il_boxIt_\d+\.Emit\(OpCodes\.)Ldarg_0\);
\s+\1Box, assembly.MainModule.TypeSystem.Int32\);
"""));
}
Expand Down
25 changes: 2 additions & 23 deletions Cecilifier.Core.Tests/Tests/Unit/LocalFunctionTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using Cecilifier.Core.Tests.Tests.Unit.Framework;
using NUnit.Framework;

Expand All @@ -15,7 +14,8 @@ public void InTopLevel_Statements([Values("static", "")] string staticOrInstance

Assert.That(
cecilifiedCode,
Does.Match(@"var m_localFoo_\d+ = new MethodDefinition\(""<<Main>\$>g__LocalFoo\|0_0\"", .+, assembly.MainModule.TypeSystem.Int32\);"));
Contains.Substring("var m_localFoo_6 = new MethodDefinition(\"<<Main>$>g__LocalFoo|0_0\", MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, assembly.MainModule.TypeSystem.Int32);"),
cecilifiedCode);

// asserts that il_topLevelMain_3 is the variable holding the ILProcessor for the top level statement body.
Assert.That(cecilifiedCode, Contains.Substring("var il_topLevelMain_3 = m_topLevelStatements_1.Body.GetILProcessor();"), cecilifiedCode);
Expand Down Expand Up @@ -97,25 +97,4 @@ public void GenericLocalFunctions_DoesNotReferencesTypeParameters_ThroughReflect
Assert.That(cecilifiedCode, Does.Match(@"il_M_\d+.Emit\(OpCodes.Box, gp_T_\d+\);"));
});
}

[Test]
public void Instanceness_IsRespected([Values] bool staticOrInstance)
{
var modifier = staticOrInstance ? "static" : string.Empty;

var result = RunCecilifier($"{modifier} int LocalFoo(int i) => i; System.Console.WriteLine(LocalFoo(42));");
var cecilifiedCode = result.GeneratedCode.ReadToEnd();

var declarationModifier = staticOrInstance ? "MethodAttributes.Static | " : String.Empty;
Assert.That(
cecilifiedCode,
Contains.Substring($"var m_localFoo_6 = new MethodDefinition(\"<<Main>$>g__LocalFoo|0_0\", MethodAttributes.Assembly | {declarationModifier}MethodAttributes.HideBySig, assembly.MainModule.TypeSystem.Int32);"),
cecilifiedCode);

var loadArgOpCode = staticOrInstance ? "Ldarg_0" : "Ldarg_1";
Assert.That(
cecilifiedCode,
Does.Match(@$"il_localFoo_\d+.Emit\(OpCodes.{loadArgOpCode}\);"),
$"Expected {loadArgOpCode} not found (looks like local function static modifier is being mishandled).");
}
}
5 changes: 3 additions & 2 deletions Cecilifier.Core.Tests/Tests/Unit/Miscellaneous.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,13 @@ namespace NS { public struct FileStream { } }
class Foo
{
public FileStream file; // System.IO.FileStream since NS.FileStream is not in scope here.
public NS.FileStream definedInFooBar;
public NS.FileStream definedInNS;
}");

var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(cecilifiedCode.Contains("FieldDefinition(\"file\", FieldAttributes.Public, st_fileStream_0);"), Is.False, cecilifiedCode);
Assert.That(cecilifiedCode.Contains("FieldDefinition(\"definedInFooBar\", FieldAttributes.Public, st_fileStream_3);"), Is.True, cecilifiedCode);
Assert.That(cecilifiedCode,Does.Match("""var st_fileStream_0 = new TypeDefinition\("NS", "FileStream", .+\);"""));
Assert.That(cecilifiedCode.Contains("FieldDefinition(\"definedInNS\", FieldAttributes.Public, st_fileStream_0);"), Is.True, cecilifiedCode);
Assert.That(cecilifiedCode.Contains("FieldDefinition(\"file\", FieldAttributes.Public, assembly.MainModule.ImportReference(typeof(System.IO.FileStream)));"), Is.True, cecilifiedCode);
}

Expand Down
Loading

0 comments on commit 08fd73f

Please sign in to comment.