Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
628214e
improves switch code generation (#233)
adrianoc Jul 28, 2023
71b9c5d
updates test expectations to match new format
adrianoc Aug 2, 2023
4d9819b
improves generated code for equality/inequality of objects against nu…
adrianoc Aug 3, 2023
6e32506
removes code duplication and moves method declaration for better orga…
adrianoc Aug 3, 2023
a74893e
fixes object initializer expression handling when used in parameterle…
adrianoc Aug 5, 2023
e11307c
fixes ldelema instruction missing target type and updates expecteatio…
adrianoc Aug 5, 2023
e9dca1c
fixes object initializer expression in array element assignment (#243)
adrianoc Aug 5, 2023
b41d9af
loads address of value type array element when accessing members of t…
adrianoc Aug 6, 2023
c4261c4
updates packages
adrianoc Aug 6, 2023
10d2b4e
refactoring improving code to add instruction after existing one to a…
adrianoc Aug 4, 2023
6d65c3c
removes unused travis configuration file
adrianoc Aug 9, 2023
d03b853
fixes local variable initializer handling for value types on expressi…
adrianoc Aug 10, 2023
53deec7
implements unboxing (#249)
adrianoc Aug 10, 2023
a6b0541
fixes instantiation of value types in ternary operators (#248)
adrianoc Aug 11, 2023
8399517
fix generated code for stobj opcode missing target type (#246)
adrianoc Aug 12, 2023
b4dbf5f
fixes methods taking nullable parameters could generate multiple Meth…
adrianoc Aug 16, 2023
5aa22e6
fixes plain value type being assigned to nullable variables in some s…
adrianoc Aug 16, 2023
4ff966d
bump version to 2.5.0
adrianoc Aug 18, 2023
f5dfb34
fixes code not awaiting on async call
adrianoc Aug 18, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .travis.yml

This file was deleted.

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>net7.0</TargetFramework>
<LangVersion>11</LangVersion>
<AssemblyVersion>2.4.1</AssemblyVersion>
<AssemblyVersion>2.5.0</AssemblyVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion Cecilifier.CommonPackages.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"><Version>4.6.0-1.final</Version></PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"><Version>4.7.0-2.final</Version></PackageReference>
<PackageReference Include="Mono.Cecil"><Version>0.11.5</Version></PackageReference>
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions Cecilifier.Core.Tests/Cecilifier.Core.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Cecilifier.Core\Cecilifier.Core.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,30 @@ IL_0006: add
IL_0007: stloc V_0
IL_000b: ldloc V_0
IL_000f: ldc.i4 0
IL_0014: beq.s IL_0036
IL_0014: beq.s IL_0031
IL_0016: ldloc V_0
IL_001a: ldc.i4 1
IL_001f: beq.s IL_0036
IL_001f: beq.s IL_0031
IL_0021: ldloc V_0
IL_0025: ldc.i4 2
IL_002a: beq.s IL_004f
IL_002c: br IL_0057
IL_0031: br IL_0064
IL_0036: nop
IL_0037: ldarg.1
IL_0038: ldc.i4 2
IL_003d: add
IL_003e: call System.Void System.Console::WriteLine(System.Int32)
IL_0043: ldc.i4 2
IL_0048: starg.s 1
IL_004a: br IL_0064
IL_004f: nop
IL_0050: ldc.i4 1
IL_0055: neg
IL_0056: ret
IL_0057: nop
IL_0058: ldc.i4 3
IL_005d: starg.s 1
IL_005f: br IL_0064
IL_0064: nop
IL_0065: ldarg.1
IL_0066: ret
IL_002a: beq.s IL_004a
IL_002c: br IL_0052
IL_0031: nop
IL_0032: ldarg.1
IL_0033: ldc.i4 2
IL_0038: add
IL_0039: call System.Void System.Console::WriteLine(System.Int32)
IL_003e: ldc.i4 2
IL_0043: starg.s 1
IL_0045: br IL_005f
IL_004a: nop
IL_004b: ldc.i4 1
IL_0050: neg
IL_0051: ret
IL_0052: nop
IL_0053: ldc.i4 3
IL_0058: starg.s 1
IL_005a: br IL_005f
IL_005f: nop
IL_0060: ldarg.1
IL_0061: ret
30 changes: 30 additions & 0 deletions Cecilifier.Core.Tests/Tests/Unit/ArrayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,34 @@ public void InitializationOptimized(string code)
\s+\4.Emit\(OpCodes.Ldtoken, \3\);
"""));
}

[TestCase("Property", "Call, m_get_2", TestName = "Property")]
[TestCase("Method()", "Call, m_method_8", TestName = "Method")]
[TestCase("Field", "Ldfld, fld_field_7", TestName = "Field")]
public void MemberAccessOnElementAccessOnValueTypeArray_LoadsElementAddress(string member, string expectedILMemberRef)
{
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\);
\s+\1Ldc_I4, 0\);
\s+\1Ldelema, st_S_0\);
\s+\1{expectedILMemberRef}\);
\s+\1Ret\);
"""));
}

[TestCase("Property", "Callvirt, m_get_2", TestName = "Property")]
[TestCase("Method()", "Callvirt, m_method_8", TestName = "Method")]
[TestCase("Field", "Ldfld, fld_field_7", TestName = "Field")]
public void MemberAccessOnElementAccessOnReferenceTypeArray_LoadsElementByReference(string member, string expectedILMemberRef)
{
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\);
\s+\1Ldc_I4, 0\);
\s+\1Ldelem_Ref\);
\s+\1{expectedILMemberRef}\);
\s+\1Ret\);
"""));
}
}
28 changes: 28 additions & 0 deletions Cecilifier.Core.Tests/Tests/Unit/CastTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using NUnit.Framework;

namespace Cecilifier.Core.Tests.Tests.Unit;

[TestFixture]
public class CastTests : CecilifierUnitTestBase
{
[Test]
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\);
\s+\1Unbox_Any, assembly.MainModule.TypeSystem.Int32\);
"""));
}

[TestCase("i", TestName = "Implicit boxing")]
[TestCase("(object) i", TestName = "Explicit boxing")]
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\);
\s+\1Box, assembly.MainModule.TypeSystem.Int32\);
"""));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void SimpleStoreToOutStructParameter(string toStore)
Assert.That(result.GeneratedCode.ReadToEnd(), Contains.Substring(
@"il_M_3.Emit(OpCodes.Ldarg_1);
il_M_3.Emit(OpCodes.Ldarg_2);
il_M_3.Emit(OpCodes.Stobj);"));
il_M_3.Emit(OpCodes.Stobj, st_myStruct_0);"));
}

[Test]
Expand Down Expand Up @@ -62,18 +62,9 @@ public void ArrayElementAssignment(string arrayVariable)
result.GeneratedCode.ReadToEnd(), Does.Match(
@"il_M_4.Emit\(OpCodes.(?:Ldarg_1|Ldfld, fld_f_2)\);
il_M_4.Emit\(OpCodes.Ldc_I4, 1\);
il_M_4.Emit\(OpCodes.Ldelema\);
il_M_4.Emit\(OpCodes.Ldelema, st_myStruct_0\);
il_M_4.Emit\(OpCodes.Initobj, st_myStruct_0\);"));
}

[Test]
public void TestParameterlessStructInstantiation()
{
var result = RunCecilifier("struct Foo { static Foo Create() => new Foo(); public Foo() { } }");
var cecilifiedCode = result.GeneratedCode.ReadToEnd();

Assert.That(cecilifiedCode, Does.Match(@"il_create_2\.Emit\(OpCodes.Newobj, ctor_foo_3\);"));
}

[TestCaseSource(nameof(InvocationOnObjectCreationExpressionTestScenarios))]
public void InvocationOnObjectCreationExpression(string invocationStatement, string expectedILSnippet)
Expand Down Expand Up @@ -112,6 +103,42 @@ public Test(int i) {}
Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match(expectedGeneratedSnippet));
}


[TestCase("var x = new S(); struct S {}",
"""
il_topLevelMain_4.Emit\(OpCodes.Ldloca_S, l_x_7\);
\s+il_topLevelMain_4.Emit\(OpCodes.Initobj, st_S_0\);
""",
TestName = "Implicit parameterless ctor")]

[TestCase("var x = new S(); struct S { public S() {} }",
"""
(il_topLevelMain_\d+.Emit\(OpCodes\.)Newobj, ctor_S_1\);
\s+\1Stloc, l_x_9\);
""",
TestName = "Explicit parameterless ctor")]

[TestCase("var x = new S(42, true); struct S { public S(int i, bool b) {} }",
"""
(il_topLevelMain_\d+.Emit\(OpCodes\.)Ldc_I4, 42\);
(\s+\1)Ldc_I4, 1\);
\2Newobj, ctor_S_1\);
""",
TestName = "Ctor with parameters")]

[TestCase("var x = new S(42, true); struct S { public S(int i, bool b) {} }",
"""
(il_topLevelMain_\d+.Emit\(OpCodes\.)Ldc_I4, 42\);
(\s+\1)Ldc_I4, 1\);
\2Newobj, ctor_S_1\);
""",
TestName = "Ctor with parameters")]
public void Instantiation_EmitsCorrectOpcode(string code, string expected)
{
var result = RunCecilifier(code);
Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match(expected));
}

static TestCaseData[] InvocationExpressionAsParametersTestScenarios()
{
return new[]
Expand Down
2 changes: 1 addition & 1 deletion Cecilifier.Core.Tests/Tests/Unit/FieldsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void TestExternalFields()

[TestCase(
"class Foo { int Value; void M() => Value = 42; }",
"Append(ldarg_0_4);", // Load this
"Emit(OpCodes.Ldarg_0);", // Load this
"Emit(OpCodes.Ldc_I4, 42);", // Load 42
"Emit(OpCodes.Stfld, fld_value_1);", // Store in Value
TestName = "Implicit This")]
Expand Down
11 changes: 5 additions & 6 deletions Cecilifier.Core.Tests/Tests/Unit/GenericTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,10 @@ public void GenericParameter_IsUsed_InsteadOfTypeOf_Issue240()
"fieldIDisp = value",
"""
//fieldIDisp = value;
\s+var ldarg_0_\d+ = (il_test_\d+\.)Create\(OpCodes.Ldarg_0\);
\s+\1Append\(ldarg_0_\d+\);
(\s+\1Emit\(OpCodes\.)Ldarg_1\);
\2Box, gp_T_\d+\);
\2Stfld, fld_fieldIDisp_\d+\);
(\s+il_test_\d+\.Emit\(OpCodes\.)Ldarg_0\);
\1Ldarg_1\);
\1Box, gp_T_\d+\);
\1Stfld, fld_fieldIDisp_\d+\);
""")]

[TestCase(
Expand Down Expand Up @@ -320,7 +319,7 @@ public void GenericParameter_IsUsed_InsteadOfTypeOf_Issue240()
\1Ldstr, "Ola Mundo"\);
\1Call, \2\);
\1Stloc, l_o_\d+\);
""")]
""")]

[TestCase(
"""Foo f = new Foo(); object o; o = f.M<T>(value)""",
Expand Down
21 changes: 10 additions & 11 deletions Cecilifier.Core.Tests/Tests/Unit/Miscellaneous.Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,35 +113,34 @@ void M()
\s+//case 1: \(condition\)
\s+il_M_7.Emit\(OpCodes.Ldloc, l_switchCondition_13\);
\s+il_M_7.Emit\(OpCodes.Ldc_I4, 1\);
\s+il_M_7.Emit\(OpCodes.Beq_S, nop_15\);
\s+il_M_7.Emit\(OpCodes.Beq_S, lbl_caseCode_0_15\);

\s+//case 2: \(condition\)
\s+il_M_7.Emit\(OpCodes.Ldloc, l_switchCondition_13\);
\s+il_M_7.Emit\(OpCodes.Ldc_I4, 2\);
\s+il_M_7.Emit\(OpCodes.Beq_S, nop_16\);
\s+il_M_7.Emit\(OpCodes.Beq_S, lbl_caseCode_1_16\);
\s+il_M_7.Emit\(OpCodes.Br, lbl_caseCode_2_17\);

\s+il_M_7.Emit\(OpCodes.Br, nop_17\);
\s+il_M_7.Emit\(OpCodes.Br, nop_14\);
\s+//case 1: \(code\)
\s+il_M_7.Append\(nop_15\);
\s+il_M_7.Append\(lbl_caseCode_0_15\);
\s+il_M_7.Emit\(OpCodes.Ldstr, "C1"\);
\s+il_M_7.Emit\(OpCodes.Call, .+System.Console.+WriteLine.+\);
\s+il_M_7.Emit\(OpCodes.Br, nop_14\);
\s+il_M_7.Emit\(OpCodes.Br, lbl_endOfSwitch_14\);

\s+//case 2: \(code\)
\s+il_M_7.Append\(nop_16\);
\s+il_M_7.Append\(lbl_caseCode_1_16\);
\s+il_M_7.Emit\(OpCodes.Ldstr, "C2"\);
\s+il_M_7.Emit\(OpCodes.Call, .+System.Console.+WriteLine.+\);
\s+il_M_7.Emit\(OpCodes.Br, nop_14\);
\s+il_M_7.Emit\(OpCodes.Br, lbl_endOfSwitch_14\);

\s+//default: \(code\)
\s+il_M_7.Append\(nop_17\);
\s+il_M_7.Append\(lbl_caseCode_2_17\);
\s+il_M_7.Emit\(OpCodes.Ldstr, "CD"\);
\s+il_M_7.Emit\(OpCodes.Call, .+System.Console.+WriteLine.+\);
\s+il_M_7.Emit\(OpCodes.Br, nop_14\);
\s+il_M_7.Emit\(OpCodes.Br, lbl_endOfSwitch_14\);

\s+//End of switch
\s+il_M_7.Append\(nop_14\);
\s+il_M_7.Append\(lbl_endOfSwitch_14\);
"""));
}

Expand Down
11 changes: 5 additions & 6 deletions Cecilifier.Core.Tests/Tests/Unit/Miscellaneous.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,11 @@ public void TestCallerArgumentExpressionAttribute_InvalidParameterName(string de
[TestCase(
"C c = new() { Value = 42 }; class C { public int Value; }",
"""
(il_topLevelMain_\d+).Emit\(OpCodes.Newobj, ctor_C_\d+\);
\s+var (dup_\d+) = \1.Create\(OpCodes.Dup\);
\s+\1.Append\(\2\);
\s+\1.Emit\(OpCodes.Ldc_I4, 42\);
\s+\1.Emit\(OpCodes.Stfld, fld_value_\d+\);
\s+\1.Emit\(OpCodes.Stloc, l_c_\d+\);
(il_topLevelMain_\d+.Emit\(OpCodes\.)Newobj, ctor_C_\d+\);
\s+\1Dup\);
\s+\1Ldc_I4, 42\);
\s+\1Stfld, fld_value_\d+\);
\s+\1Stloc, l_c_\d+\);
""",
TestName = "Object Initializer")]
public void TestImplicitObjectCreation(string code, params string[] expectations)
Expand Down
Loading