-
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathAttributesTest.cs
210 lines (181 loc) · 11.3 KB
/
AttributesTest.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
using System.Text.RegularExpressions;
using Cecilifier.Core.Tests.Tests.Unit.Framework;
using NUnit.Framework;
namespace Cecilifier.Core.Tests.Tests.Unit;
[TestFixture]
public class AttributesTest : CecilifierUnitTestBase
{
[Test]
public void TestAttributeAppliedToItsOwnType()
{
var result = RunCecilifier($"[My(\"type\")] {AttributeDefinition}");
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(cecilifiedCode, Does.Match(@"var ctor_myAttribute_\d+ = new MethodDefinition\("".ctor"", MethodAttributes.Private, assembly.MainModule.TypeSystem.Void\);")); // This represents the ctor with argument
Assert.That(cecilifiedCode, Does.Not.Match(@"var ctor_myAttribute_\d+ = new MethodDefinition\("".ctor"", MethodAttributes.Public \| MethodAttributes.HideBySig \| MethodAttributes.RTSpecialName \| MethodAttributes.SpecialName, assembly.MainModule.TypeSystem.Void\);"), "Parameterless ctor not expected");
Assert.That(cecilifiedCode, Does.Match(@"cls_myAttribute_\d+\.CustomAttributes.Add\(attr_my_\d+\);"), "Custom attribute should be be added");
Assert.That(cecilifiedCode, Does.Match(@"var attr_my_\d+ = new CustomAttribute\(ctor_myAttribute_\d+\);"), "Reference to class declaring MyAttribute should be used when instantiating the custom attribute");
}
[Test]
public void TestAttributeAppliedToAssembly()
{
var result = RunCecilifier($"[assembly:My(\"assembly\")] {AttributeDefinition}");
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(cecilifiedCode, Does.Match(ClassDeclarationRegexFor("MyAttribute", "cls_myAttribute", ".+", "TypeAttributes.NotPublic")), "Expected TypeDefinition for attribute not found.");
Assert.That(cecilifiedCode, Does.Match(@"var attr_my_1 = new CustomAttribute\(ctor_myAttribute_2\);"), "Reference to class declaring MyAttribute should be used when instantiating the custom attribute");
Assert.That(cecilifiedCode, Does.Match(@"assembly.CustomAttributes.Add\(attr_my_1\);"), "Custom attribute should be be added to assembly");
}
[TestCase("class Foo<[My(\"Type\")] T> {{ }} {0}", TestName = "Type")]
[TestCase("class Foo {{ void M<[My(\"Method\")] T>() {{ }} }} {0}", TestName = "Method")]
public void TestAttributeAppliedToTypeParameters(string attributeUsage)
{
var result = RunCecilifier(string.Format(attributeUsage, AttributeDefinition));
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(cecilifiedCode, Does.Match(@"var gp_T_\d+ = new Mono.Cecil.GenericParameter\(""T"", .+\);"));
Assert.That(cecilifiedCode, Does.Match(@"gp_T_\d+.CustomAttributes.Add\(attr_my_\d+\);"));
}
[Test]
public void TestGenericAttributeDefinition()
{
var result = RunCecilifier(GenericAttributeDefinition);
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(cecilifiedCode, Does.Match(
@"(?s)(cls_myGenericAttribute_\d+) = new TypeDefinition\(.+""MyGenericAttribute`1"".+,.+ImportReference\(typeof\(System.Attribute\)\)\);" +
@"\s+.+(gp_T_\d+) = new Mono.Cecil.GenericParameter\(""T"", \1\);" +
@"\s+.+\1.GenericParameters.Add\(\2\);"));
}
[TestCase("[MyGeneric<int>]", "Int32", TestName="Value Type")]
[TestCase("[MyGeneric<string>]", "String", TestName = "Reference Type")]
[TestCase("[MyGeneric<Foo<int>>]", @"cls_foo_\d+.MakeGenericInstanceType\(.+Int32\)", TestName = "Generic Type")]
public void TestGenericAttributeUsage(string attribute, string expectedType)
{
var result = RunCecilifier($@"
{GenericAttributeDefinition}
{attribute}
class Foo<TFoo> {{ }}");
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(
cecilifiedCode,
Does.Match(
$@"(?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)]
[TestCase("LayoutKind.Auto, Size=42", 0, 42)]
[TestCase("LayoutKind.Sequential")]
public void StructLayout_ItNotEmitted(string initializationData, int expectedPack = -1, int expectedSize = -1)
{
// StructLayout attribute should not be emitted to metadata as an attribute;
// instead, the respective properties in the type definition should be set.
var result = RunCecilifier($$"""
using System.Runtime.InteropServices;
[StructLayout({{initializationData}})]
struct Foo { }
""");
var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(cecilifiedCode, Does.Not.Match(@"st_foo_\d+.CustomAttributes.Add\(attr_structLayout_\d+\);"));
if (expectedSize == -1 && expectedPack == -1)
{
Assert.That(cecilifiedCode, Does.Not.Match(@"st_foo_\d+.ClassSize = \d+;"));
Assert.That(cecilifiedCode, Does.Not.Match(@"st_foo_\d+.PackingSize = \d+;"));
}
else
{
Assert.That(cecilifiedCode, Does.Match(@$"st_foo_\d+.ClassSize = {expectedSize};"));
Assert.That(cecilifiedCode, Does.Match(@$"st_foo_\d+.PackingSize = {expectedPack};"));
}
Assert.That(cecilifiedCode, Does.Match($@"\|\s+TypeAttributes.{Regex.Match(initializationData, @"LayoutKind\.([^,$]+)").Groups[1].Value}Layout"));
}
private const string AttributeDefinition = "class MyAttribute : System.Attribute { public MyAttribute(string message) {} } ";
private const string GenericAttributeDefinition = @"
[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple =true)]
class MyGenericAttribute<T> : System.Attribute
{
public MyGenericAttribute() {}
public MyGenericAttribute(T value) {}
public T Value {get; set; }
}";
}