Skip to content

Commit 08fd73f

Browse files
authored
Merge pull request #329 from adrianoc/staging
February 2025 Update 1 (Version 2.18)
2 parents acd1333 + 01f9120 commit 08fd73f

43 files changed

Lines changed: 687 additions & 310 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cecilifier.Common.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<TargetFramework>net8.0</TargetFramework>
44
<LangVersion>12</LangVersion>
5-
<AssemblyVersion>2.17.0</AssemblyVersion>
5+
<AssemblyVersion>2.18.0</AssemblyVersion>
66
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
77
</PropertyGroup>
88
</Project>

Cecilifier.Core.Tests/Tests/Integration/CodeBlock.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,6 @@ public void NestedIfStatementTest()
2424
AssertResourceTestWithExplicitExpectation(@"CodeBlock/Conditional/NestedIfStatement", "System.Void NestedIfStatement::Foo(System.Int32)");
2525
}
2626

27-
[Test]
28-
[Ignore("Not Implemented yet")]
29-
public void NullCoalescingTest()
30-
{
31-
AssertResourceTest(@"CodeBlock/Conditional/");
32-
}
33-
3427
[Test]
3528
[Ignore("Not Implemented yet")]
3629
public void SwitchStatementTest()

Cecilifier.Core.Tests/Tests/Integration/GenericsTestCase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ public void TestInstanceNonGenericMethodsOnGenericTypes()
2626
[Test]
2727
public void TestGenericInferredStaticMethods()
2828
{
29-
AssertResourceTest(@"Generics/StaticInferredMethods");
29+
AssertResourceTest("Generics/StaticInferredMethods");
3030
}
3131

3232
[Test]
3333
public void TestGenericExplicitStaticMethods()
3434
{
35-
AssertResourceTest(@"Generics/StaticExplicitMethods");
35+
AssertResourceTest("Generics/StaticExplicitMethods");
3636
}
3737

3838
[Test]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Cecilifier.Core.Tests.Framework;
2+
using NUnit.Framework;
3+
4+
namespace Cecilifier.Core.Tests.OutputBased;
5+
6+
[TestFixture]
7+
public class LocalFunctionTests : OutputBasedTestBase
8+
{
9+
[TestCase("static", TestName = "Static")]
10+
[TestCase("", TestName = "Instance")]
11+
public void InstanceLocalFunction(string staticOrInstance)
12+
{
13+
AssertOutput($"""
14+
System.Console.Write(M(1));
15+
{staticOrInstance} int M(int i) => 41 + i;
16+
""",
17+
"42");
18+
}
19+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using Cecilifier.Core.Tests.Framework;
2+
using NUnit.Framework;
3+
4+
namespace Cecilifier.Core.Tests.OutputBased;
5+
6+
[TestFixture]
7+
public class NullCoalescingOperatorTests : OutputBasedTestBase
8+
{
9+
[Test]
10+
public void SimpleNullableValueType()
11+
{
12+
AssertOutput("""
13+
System.Console.Write(M(42, 1));
14+
int? M(int? i1, int? i2) => i1 ?? i2;
15+
""",
16+
"42");
17+
}
18+
19+
[Test]
20+
public void SimpleReferenceType()
21+
{
22+
AssertOutput("""
23+
System.Console.Write(M(null, "42"));
24+
object M(object o1, object o2) => o1 ?? o2;
25+
""",
26+
"42");
27+
}
28+
29+
[Test]
30+
public void MixedNullableValueType_AndReferenceType()
31+
{
32+
AssertOutput("""
33+
var r = M3(42, 1);
34+
System.Console.Write(r.Value);
35+
36+
int? M3(int? i1, object i2) => i1 ?? (int) i2;
37+
""",
38+
"42");
39+
}
40+
41+
[TestCase("(int?) o1 ?? (int?) o2")]
42+
[TestCase("(int?) o1 ?? (int) o2")]
43+
public void Convoluted(string coalescingExpression)
44+
{
45+
AssertOutput($"""
46+
var r = M(42, 1);
47+
System.Console.Write(r.Value);
48+
49+
int? M(object o1, object o2) => {coalescingExpression};
50+
""",
51+
"42");
52+
}
53+
54+
[TestCase(null, null, "C", "C")]
55+
[TestCase(null, "B", null, "B")]
56+
[TestCase(null, "B", "C", "B")]
57+
[TestCase("A", "B", "C", "A")]
58+
[TestCase("A", "B", null, "A")]
59+
public void TestAssociative(string a, string b, string c, string expectedOutput)
60+
{
61+
AssertOutput($"System.Console.Write({Quote(a)} ?? {Quote(b)} ?? {Quote(c)});", expectedOutput);
62+
63+
string? Quote(string s) => s == null ? "null" : $"\"{s}\"";
64+
}
65+
}

Cecilifier.Core.Tests/Tests/Unit/ArrayTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public void MemberAccessOnElementAccessOnValueTypeArray_LoadsElementAddress(stri
172172
{
173173
var result = RunCecilifier($$"""int M(S[] sa) => sa[0].{{member}}; struct S { public int Property { get; set; } public int Field; public int Method() => 1; }""");
174174
Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match($"""
175-
(il_M_\d+\.Emit\(OpCodes\.)Ldarg_1\);
175+
(il_M_\d+\.Emit\(OpCodes\.)Ldarg_0\);
176176
\s+\1Ldc_I4, 0\);
177177
\s+\1Ldelema, st_S_0\);
178178
\s+\1{expectedILMemberRef}\);
@@ -187,7 +187,7 @@ public void MemberAccessOnElementAccessOnReferenceTypeArray_LoadsElementByRefere
187187
{
188188
var result = RunCecilifier($$"""int M(S[] sa) => sa[0].{{member}}; class S { public int Property { get; set; } public int Field; public int Method() => 1; }""");
189189
Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match($"""
190-
(il_M_\d+\.Emit\(OpCodes\.)Ldarg_1\);
190+
(il_M_\d+\.Emit\(OpCodes\.)Ldarg_0\);
191191
\s+\1Ldc_I4, 0\);
192192
\s+\1Ldelem_Ref\);
193193
\s+\1{expectedILMemberRef}\);

Cecilifier.Core.Tests/Tests/Unit/AttributesTest.cs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,100 @@ class Foo<TFoo> {{ }}");
7070
$@"(?s)var (attr_myGeneric_1_\d+) = new CustomAttribute\(new MethodReference\((ctor_myGenericAttribute_\d+)\.Name.+\2\.ReturnType\).+DeclaringType = cls_myGenericAttribute_\d+.MakeGenericInstanceType\(.*{expectedType}\).+\);\s+" +
7171
@"cls_foo_\d+\.CustomAttributes\.Add\(\1\);"));
7272
}
73+
74+
[Test]
75+
public void ForwardReferenceToGenericAttributeWorks()
76+
{
77+
var result = RunCecilifier($$"""
78+
[MyGeneric<int>]
79+
class Foo<TFoo> { }
80+
{{GenericAttributeDefinition}}
81+
""");
82+
83+
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
84+
Assert.That(
85+
cecilifiedCode,
86+
Does.Match("""
87+
\s+assembly\.MainModule\.Types\.Add\((?<targetType>cls_foo_\d+)\);
88+
\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\), .+\);
89+
\s+cls_foo_22.CustomAttributes.Add\(\k<attr>\);
90+
"""));
91+
}
92+
93+
[Test]
94+
public void ForwardReferenceToGenericAttributeWorks2()
95+
{
96+
var result = RunCecilifier($$"""
97+
class Foo
98+
{
99+
[MyGeneric<int>]
100+
void M() {}
101+
}
102+
{{GenericAttributeDefinition}}
103+
""");
104+
105+
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
106+
Assert.That(
107+
cecilifiedCode,
108+
Does.Match("""
109+
\s+var (?<m>m_M_\d+) = new MethodDefinition\("M",.+\);
110+
\s+cls_foo_\d+.Methods.Add\(\k<m>\);
111+
\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\), .+\);
112+
\s+\k<m>.CustomAttributes.Add\(\k<attr>\);
113+
"""));
114+
}
115+
116+
[Test]
117+
public void CyclicForwardReferenceToGenericAttributeWorks1()
118+
{
119+
var result = RunCecilifier("""
120+
[MyGeneric<Foo>]
121+
class Foo { }
122+
123+
class MyGenericAttribute<T> : System.Attribute {}
124+
""");
125+
126+
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
127+
Assert.That(
128+
cecilifiedCode,
129+
Does.Match("""
130+
\s+assembly\.MainModule\.Types\.Add\((?<targetType>cls_foo_\d+)\);
131+
\s+var (?<attr>attr_myGeneric_\d+_\d+) = new CustomAttribute\(.+(?<attrCtor>ctor_myGenericAttribute_\d+).Name, \k<attrCtor>.ReturnType\) {.+DeclaringType = cls_myGenericAttribute_\d+.MakeGenericInstanceType\(\k<targetType>\), .+\);
132+
\s+cls_foo_\d+.CustomAttributes.Add\(\k<attr>\);
133+
"""));
134+
}
135+
136+
[Test]
137+
public void CyclicForwardReferenceToGenericAttributeWorks2()
138+
{
139+
var result = RunCecilifier("""
140+
[MyGeneric<int>]
141+
class Foo { }
142+
143+
class MyGenericAttribute<T> : System.Attribute
144+
{
145+
public Foo _foo;
146+
}
147+
""");
148+
149+
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
150+
Assert.That(
151+
cecilifiedCode,
152+
Does.Match("""
153+
//Class : Foo
154+
\s+var (?<appliedTo>cls_foo_\d+) = new TypeDefinition\("", "Foo",.+\);
155+
\s+assembly.MainModule.Types.Add\(\k<appliedTo>\);
156+
157+
\s+//Class : MyGenericAttribute
158+
\s+var (?<attrType>cls_myGenericAttribute_\d+) = new TypeDefinition\("", "MyGenericAttribute`1",.+ImportReference\(typeof\(System.Attribute\)\)\);
159+
\s+var gp_T_\d+ = new Mono.Cecil.GenericParameter\("T", \k<attrType>\);
160+
\s+\k<attrType>.GenericParameters.Add\(gp_T_\d+\);
161+
\s+assembly.MainModule.Types.Add\(\k<attrType>\);
162+
\s+var ctor_myGenericAttribute_4 = new MethodDefinition\(".ctor",.+TypeSystem.Void\);
163+
\s+var (?<attrInstance>attr_myGeneric_1_\d+) = new CustomAttribute\(new MethodReference\(ctor_myGenericAttribute_4.Name, ctor_myGenericAttribute_4.ReturnType\) {.+DeclaringType = \k<attrType>.MakeGenericInstanceType\(.+Int32\).+}\);
164+
\s+\k<appliedTo>.CustomAttributes.Add\(\k<attrInstance>\);
165+
"""));
166+
}
73167

74168
[TestCase("LayoutKind.Auto, Pack=1, Size=12", 1, 12)]
75169
[TestCase("LayoutKind.Auto, Pack=1", 1, 0)]

Cecilifier.Core.Tests/Tests/Unit/CastTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public void Unbox()
1111
{
1212
var result = RunCecilifier("int UnboxIt(object o) => (int) o;");
1313
Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match("""
14-
(il_unboxIt_\d+\.Emit\(OpCodes\.)Ldarg_1\);
14+
(il_unboxIt_\d+\.Emit\(OpCodes\.)Ldarg_0\);
1515
\s+\1Unbox_Any, assembly.MainModule.TypeSystem.Int32\);
1616
"""));
1717
}
@@ -22,7 +22,7 @@ public void Box(string expression)
2222
{
2323
var result = RunCecilifier($"object BoxIt(int i) => {expression};");
2424
Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match("""
25-
(il_boxIt_\d+\.Emit\(OpCodes\.)Ldarg_1\);
25+
(il_boxIt_\d+\.Emit\(OpCodes\.)Ldarg_0\);
2626
\s+\1Box, assembly.MainModule.TypeSystem.Int32\);
2727
"""));
2828
}

Cecilifier.Core.Tests/Tests/Unit/LocalFunctionTests.cs

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using Cecilifier.Core.Tests.Tests.Unit.Framework;
32
using NUnit.Framework;
43

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

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

2020
// asserts that il_topLevelMain_3 is the variable holding the ILProcessor for the top level statement body.
2121
Assert.That(cecilifiedCode, Contains.Substring("var il_topLevelMain_3 = m_topLevelStatements_1.Body.GetILProcessor();"), cecilifiedCode);
@@ -97,25 +97,4 @@ public void GenericLocalFunctions_DoesNotReferencesTypeParameters_ThroughReflect
9797
Assert.That(cecilifiedCode, Does.Match(@"il_M_\d+.Emit\(OpCodes.Box, gp_T_\d+\);"));
9898
});
9999
}
100-
101-
[Test]
102-
public void Instanceness_IsRespected([Values] bool staticOrInstance)
103-
{
104-
var modifier = staticOrInstance ? "static" : string.Empty;
105-
106-
var result = RunCecilifier($"{modifier} int LocalFoo(int i) => i; System.Console.WriteLine(LocalFoo(42));");
107-
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
108-
109-
var declarationModifier = staticOrInstance ? "MethodAttributes.Static | " : String.Empty;
110-
Assert.That(
111-
cecilifiedCode,
112-
Contains.Substring($"var m_localFoo_6 = new MethodDefinition(\"<<Main>$>g__LocalFoo|0_0\", MethodAttributes.Assembly | {declarationModifier}MethodAttributes.HideBySig, assembly.MainModule.TypeSystem.Int32);"),
113-
cecilifiedCode);
114-
115-
var loadArgOpCode = staticOrInstance ? "Ldarg_0" : "Ldarg_1";
116-
Assert.That(
117-
cecilifiedCode,
118-
Does.Match(@$"il_localFoo_\d+.Emit\(OpCodes.{loadArgOpCode}\);"),
119-
$"Expected {loadArgOpCode} not found (looks like local function static modifier is being mishandled).");
120-
}
121100
}

Cecilifier.Core.Tests/Tests/Unit/Miscellaneous.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,13 @@ namespace NS { public struct FileStream { } }
7070
class Foo
7171
{
7272
public FileStream file; // System.IO.FileStream since NS.FileStream is not in scope here.
73-
public NS.FileStream definedInFooBar;
73+
public NS.FileStream definedInNS;
7474
}");
7575

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

0 commit comments

Comments
 (0)