Skip to content

Commit

Permalink
Merge pull request #244 from adrianoc/vnext
Browse files Browse the repository at this point in the history
July 2023 updates
  • Loading branch information
adrianoc authored Jul 22, 2023
2 parents 5fda2cf + cdc809c commit 214eb89
Show file tree
Hide file tree
Showing 40 changed files with 1,897 additions and 351 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>net7.0</TargetFramework>
<LangVersion>11</LangVersion>
<AssemblyVersion>2.02.0</AssemblyVersion>
<AssemblyVersion>2.4.1</AssemblyVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class UsageOfNonGenericMethodOnGenericTypeFromExternalAssembly
{
bool M<T>(System.IEquatable<T> lhs, T rhs) => lhs.Equals(rhs);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using System.Collections.Generic;
class UsageOfNonGenericMethodOnGenericTypeFromExternalAssembly2
{
IEnumerator<T> M<T>(IEnumerable<T> e) => e.GetEnumerator();
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class CtorWithParameters : Base
private static string Process(int n, int j)
{
new CtorWithParameters(10).Sum(1, 2);
return (n + j).ToString();
return "not important";
}

private int Sum(int i, int j)
Expand Down
7 changes: 7 additions & 0 deletions Cecilifier.Core.Tests/Tests/Integration/GenericsTestCase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,12 @@ public void TestUsageOfNonGenericMethodOnGenericType()
{
AssertResourceTest("Generics/UsageOfNonGenericMethodOnGenericType");
}

[TestCase("UsageOfNonGenericMethodOnGenericTypeFromExternalAssembly")]
[TestCase("UsageOfNonGenericMethodOnGenericTypeFromExternalAssembly2")]
public void UsageOfNonGenericMethodOnGenericTypeFromExternalAssembly(string testName)
{
AssertResourceTest($"Generics/{testName}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public void TupleExpression(string tuple)
AssertUnsupportedFeature($"class Foo {{ public (int, bool) F() {{ return (1, true); }} void M() {{ {tuple} = F(); }} }}", "Syntax 'TupleExpression' is not supported");
}

[TestCase("foreach(var i in ints);", "ForEachStatement", TestName = "ForEachStatement")]
[TestCase("while(true);", "WhileStatement", TestName = "WhileStatement")]
[TestCase("lock(ints) {}", "LockStatement", TestName = "LockStatement")]
[TestCase("unsafe {}", "UnsafeStatement", TestName = "UnsafeStatement")]
Expand Down
31 changes: 16 additions & 15 deletions Cecilifier.Core.Tests/Tests/Unit/ArrayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,21 @@ public void InitializationOptimized(string code)
var result = RunCecilifier(code);
var cecilifiedCode = result.GeneratedCode.ReadToEnd();

Assert.That(cecilifiedCode, Does.Match("""
\s+//<PrivateImplementationDetails> class.
\s+//This type is emitted by the compiler.
\s+var (cls_privateImplementationDetails_\d+) = new TypeDefinition\("", "<PrivateImplementationDetails>", TypeAttributes.NotPublic \| TypeAttributes.Sealed \| TypeAttributes.AnsiClass \| TypeAttributes.AutoLayout, assembly.MainModule.TypeSystem.Object\);
\s+assembly.MainModule.Types.Add\(\1\);
\s+//__StaticArrayInitTypeSize=12 struct.
\s+//This struct is emitted by the compiler and is used to hold raw data used in arrays/span initialization optimizations
\s+var (st_rawDataTypeVar_\d+) = new TypeDefinition\("", "__StaticArrayInitTypeSize=12", TypeAttributes.NestedPrivate \| TypeAttributes.Sealed \| TypeAttributes.AnsiClass \| TypeAttributes.ExplicitLayout.+\) \{ ClassSize = 12,PackingSize = 1 \};
\s+\1.NestedTypes.Add\(\2\);
\s+var (fld_arrayInitializerData_\d+) = new FieldDefinition\("[A-Z0-9]+", FieldAttributes.Assembly \| FieldAttributes.Static \| FieldAttributes.InitOnly, \2\);
\s+\1.Fields.Add\(\3\);
\s+\3.InitialValue = Cecilifier.Runtime.TypeHelpers.ToByteArray<Int32>\(new Int32\[\] { 1, 2, 3 }\);
\s+(il_.+).Emit\(OpCodes.Dup\);
\s+\4.Emit\(OpCodes.Ldtoken, \3\);
"""));
Assert.That(
cecilifiedCode, Does.Match("""
\s+//<PrivateImplementationDetails> class.
\s+//This type is emitted by the compiler.
\s+var (cls_privateImplementationDetails_\d+) = new TypeDefinition\("", "<PrivateImplementationDetails>", TypeAttributes.NotPublic \| TypeAttributes.Sealed \| TypeAttributes.AnsiClass \| TypeAttributes.AutoLayout, assembly.MainModule.TypeSystem.Object\);
\s+assembly.MainModule.Types.Add\(\1\);
\s+//__StaticArrayInitTypeSize=12 struct.
\s+//This struct is emitted by the compiler and is used to hold raw data used in arrays/span initialization optimizations
\s+var (st_rawDataTypeVar_\d+) = new TypeDefinition\("", "__StaticArrayInitTypeSize=12", TypeAttributes.NestedPrivate \| TypeAttributes.Sealed \| TypeAttributes.AnsiClass \| TypeAttributes.ExplicitLayout.+\) \{ ClassSize = 12,PackingSize = 1 \};
\s+\1.NestedTypes.Add\(\2\);
\s+var (fld_arrayInitializerData_\d+) = new FieldDefinition\("[A-Z0-9]+", FieldAttributes.Assembly \| FieldAttributes.Static \| FieldAttributes.InitOnly, \2\);
\s+\1.Fields.Add\(\3\);
\s+\3.InitialValue = Cecilifier.Runtime.TypeHelpers.ToByteArray<Int32>\(new Int32\[\] { 1, 2, 3 }\);
\s+(il_.+).Emit\(OpCodes.Dup\);
\s+\4.Emit\(OpCodes.Ldtoken, \3\);
"""));
}
}
206 changes: 206 additions & 0 deletions Cecilifier.Core.Tests/Tests/Unit/CustomValueTypesInstantiationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,210 @@ public void TestParameterlessStructInstantiation()

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

[TestCaseSource(nameof(InvocationOnObjectCreationExpressionTestScenarios))]
public void InvocationOnObjectCreationExpression(string invocationStatement, string expectedILSnippet)
{
var result = RunCecilifier($$"""
using System;

{{invocationStatement}};

struct Test : IDisposable
{
public Test(int i) {}
public void M() {}
public void Dispose() {}
}
""");

Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match(expectedILSnippet));
}

[TestCaseSource(nameof(InvocationExpressionAsParametersTestScenarios))]
public void TestInvocationExpressionAsParameters(string testStatement, string expectedGeneratedSnippet)
{
var result = RunCecilifier($$"""
{{testStatement}};

void ByValue(Test t) {}
void AsIn(in Test t) {}

struct Test
{
public Test(int i) {}
}
""");

Assert.That(result.GeneratedCode.ReadToEnd(), Does.Match(expectedGeneratedSnippet));
}

static TestCaseData[] InvocationExpressionAsParametersTestScenarios()
{
return new[]
{
new TestCaseData(
"ByValue(new Test())",
$"""
(il_topLevelMain_\d+\.Emit\(OpCodes\.)Ldloca_S, (l_vt_\d+)\);
\s+\1Initobj, st_test_0\);
\s+\1Ldloc, \2\);
\s+\1Call, .+\);
""").SetName("by value"),

new TestCaseData(
"AsIn(new Test())",
$"""
(il_topLevelMain_\d+\.Emit\(OpCodes\.)Ldloca_S, (l_vt_\d+)\);
\s+\1Initobj, st_test_0\);
\s+\1Ldloca, \2\);
\s+\1Call, .+\);
""").SetName("as in implicit ctor"),

new TestCaseData(
"AsIn(new Test(42))",
$"""
(il_topLevelMain_\d+\.Emit\(OpCodes\.)Ldc_I4, 42\);
\s+\1Newobj, ctor_test_\d+\);
\s+var (l_tmp_\d+) = new VariableDefinition\(st_test_\d+\);
\s+m_topLevelStatements_\d+.Body.Variables.Add\(\2\);
\s+\1Stloc, \2\);
\s+\1Ldloca_S, \2\);
\s+\1Call, .+\);
""").SetName("as in explicit ctor"),
};
}

static TestCaseData[] InvocationOnObjectCreationExpressionTestScenarios()
{
return new[]
{
new TestCaseData(
"new Test().M()",
$"""
(m_topLevelStatements_\d+).Body.Variables.Add\((l_vt_\d+)\);
\s+(il_topLevelMain_\d+.Emit\(OpCodes\.)Ldloca_S, \2\);
\s+\3Initobj, st_test_0\);
\s+\3Ldloca, \2\);
\s+\3Call, m_M_\d+\);
\s+\1\.Body\.Instructions\.Add\(il_topLevelMain_\d+.Create\(OpCodes.Ret\)\);
""").SetName("Implicit: direct call own method"),

new TestCaseData(
"new Test().Dispose()",
"""
(m_topLevelStatements_\d+).Body.Variables.Add\((l_vt_\d+)\);
\s+(il_topLevelMain_\d+.Emit\(OpCodes\.)Ldloca_S, \2\);
\s+\3Initobj, st_test_0\);
\s+\3Ldloca, \2\);
\s+\3Call, m_dispose_\d+\);
\s+\1\.Body\.Instructions\.Add\(il_topLevelMain_\d+.Create\(OpCodes.Ret\)\);
""").SetName("Implicit: direct interface method"),

new TestCaseData(
"((IDisposable) new Test()).Dispose()",
"""
(m_topLevelStatements_\d+).Body.Variables.Add\((l_vt_\d+)\);
\s+(il_topLevelMain_\d+.Emit\(OpCodes\.)Ldloca_S, \2\);
\s+\3Initobj, st_test_0\);
\s+\3Ldloc, \2\);
\s+\3Box, st_test_0\);
\s+\3Callvirt, .+"Dispose".+\);
\s+\1\.Body\.Instructions\.Add\(il_topLevelMain_\d+.Create\(OpCodes.Ret\)\);
""").SetName("Implicit: call through interface cast"),

new TestCaseData(
"new Test().GetHashCode()",
"""
(m_topLevelStatements_\d+).Body.Variables.Add\((l_vt_\d+)\);
\s+(il_topLevelMain_\d+.Emit\(OpCodes\.)Ldloca_S, \2\);
\s+\3Initobj, st_test_0\);
\s+\3Ldloca, \2\);
\s+\3Constrained, st_test_0\);
\s+\3Callvirt, .+GetHashCode.+\);
\s+\3Pop\);
\s+\1\.Body\.Instructions\.Add\(il_topLevelMain_\d+.Create\(OpCodes.Ret\)\);
""").SetName("Implicit: direct object method call"),

new TestCaseData(
"((Object) new Test()).GetHashCode()",
"""
(m_topLevelStatements_\d+).Body.Variables.Add\((l_vt_\d+)\);
\s+(il_topLevelMain_\d+.Emit\(OpCodes\.)Ldloca_S, \2\);
\s+\3Initobj, st_test_0\);
\s+\3Ldloc, \2\);
\s+\3Box, st_test_0\);
\s+\3Callvirt, .+GetHashCode.+\);
\s+\3Pop\);
\s+\1\.Body\.Instructions\.Add\(il_topLevelMain_\d+.Create\(OpCodes.Ret\)\);
""").SetName("Implicit: call through object cast"),

new TestCaseData(
"((IDisposable) new Test(1)).Dispose()",
"""
(il_topLevelMain_\d+\.Emit\(OpCodes\.)Ldc_I4, 1\);
\s+\1Newobj, ctor_test_\d+\);
\s+var (l_tmp_\d+) = new VariableDefinition\(st_test_\d+\);
\s+m_topLevelStatements_\d+.Body.Variables.Add\(\2\);
\s+\1Stloc, \2\);
\s+\1Ldloc, \2\);
\s+\1Box, st_test_\d+\);
\s+\1Callvirt, .+"Dispose".+\);
""").SetName("Explicit: call through interface cast"),

new TestCaseData(
"new Test(1).GetHashCode()",
"""
(il_topLevelMain_\d+\.Emit\(OpCodes\.)Ldc_I4, 1\);
\s+\1Newobj, ctor_test_\d+\);
\s+var (l_tmp_\d+) = new VariableDefinition\(st_test_\d+\);
\s+m_topLevelStatements_\d+.Body.Variables.Add\(\2\);
\s+\1Stloc, \2\);
\s+\1Ldloca_S, \2\);
\s+\1Constrained, st_test_\d+\);
\s+\1Callvirt, .+"GetHashCode".+\);
\s+\1Pop\);
""").SetName("Explicit: direct object method call"), // Only missing constrained

new TestCaseData(
"((object)new Test(1)).GetHashCode()",
"""
(il_topLevelMain_\d+\.Emit\(OpCodes\.)Ldc_I4, 1\);
\s+\1Newobj, ctor_test_\d+\);
\s+var (l_tmp_\d+) = new VariableDefinition\(st_test_\d+\);
\s+m_topLevelStatements_\d+.Body.Variables.Add\(\2\);
\s+\1Stloc, \2\);
\s+\1Ldloc, \2\);
\s+\1Box, st_test_\d+\);
\s+\1Callvirt, .+"GetHashCode".+\);
\s+\1Pop\);
""").SetName("Explicit: call through object cast"),

new TestCaseData(
"new Test(1).M()",
$"""
(il_topLevelMain_\d+\.Emit\(OpCodes\.)Ldc_I4, 1\);
\s+\1Newobj, ctor_test_\d+\);
\s+var (l_tmp_\d+) = new VariableDefinition\(st_test_\d+\);
\s+m_topLevelStatements_\d+.Body.Variables.Add\(\2\);
\s+\1Stloc, \2\);
\s+\1Ldloca_S, \2\);
\s+\1Call, m_M_\d+\);
\s+m_topLevelStatements_\d+\.Body\.Instructions\.Add\(il_topLevelMain_\d+.Create\(OpCodes.Ret\)\);
""").SetName("Explicit: direct call own method"),

new TestCaseData(
"new Test(1).Dispose()",
"""
(il_topLevelMain_\d+\.Emit\(OpCodes\.)Ldc_I4, 1\);
\s+\1Newobj, ctor_test_\d+\);
\s+var (l_tmp_\d+) = new VariableDefinition\(st_test_\d+\);
\s+m_topLevelStatements_\d+.Body.Variables.Add\(\2\);
\s+\1Stloc, \2\);
\s+\1Ldloca_S, \2\);
\s+\1Call, m_dispose_\d+\);
\s+m_topLevelStatements_\d+\.Body\.Instructions\.Add\(il_topLevelMain_\d+.Create\(OpCodes.Ret\)\);
""").SetName("Explicit: direct interface method"),
};
}
}
Loading

0 comments on commit 214eb89

Please sign in to comment.