Skip to content

Commit 16a14a5

Browse files
authored
Fix processing of auto backing fields (#180)
1 parent 17793f0 commit 16a14a5

File tree

10 files changed

+164
-44
lines changed

10 files changed

+164
-44
lines changed

MetadataProcessor.Shared/SkeletonGenerator/AssemblyClass.cs

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public class StaticField
3535
{
3636
public string Name;
3737
public int ReferenceIndex;
38+
public string FieldWarning;
3839
}
3940

4041
public class InstanceField

MetadataProcessor.Shared/SkeletonGenerator/SkeletonTemplates.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//
1+
//
22
// Copyright (c) .NET Foundation and Contributors
33
// See LICENSE file in the project root for full license information.
44
//
@@ -62,6 +62,9 @@ struct Library_{{AssemblyName}}_{{Name}}{{#newline}}
6262
{{{#newline}}
6363
6464
{{#each StaticFields}}
65+
{{#if FieldWarning}}
66+
{{FieldWarning}}{{#newline}}
67+
{{/if}}
6568
static const int FIELD_STATIC__{{Name}} = {{ReferenceIndex}};{{#newline}}
6669
{{/each}}
6770
{{#if StaticFields}}{{#newline}}{{/if}}

MetadataProcessor.Shared/nanoSkeletonGenerator.cs

+29-13
Original file line numberDiff line numberDiff line change
@@ -465,14 +465,17 @@ private void GenerateAssemblyHeader()
465465

466466
// static fields
467467
int fieldCount = 0;
468-
var staticFields = c.Fields.Where(f => f.IsStatic && !f.IsLiteral);
468+
IEnumerable<FieldDefinition> staticFields = c.Fields.Where(f => f.IsStatic && !f.IsLiteral);
469469

470-
foreach (var f in staticFields)
470+
foreach (FieldDefinition f in staticFields)
471471
{
472+
FixFieldName(f, out string fixedFieldName, out string fieldWarning);
473+
472474
classData.StaticFields.Add(new StaticField()
473475
{
474-
Name = f.Name,
475-
ReferenceIndex = staticFieldCount + fieldCount++
476+
Name = string.IsNullOrEmpty(fixedFieldName) ? f.Name : fixedFieldName,
477+
ReferenceIndex = staticFieldCount + fieldCount++,
478+
FieldWarning = fieldWarning
476479
});
477480
}
478481

@@ -488,15 +491,7 @@ private void GenerateAssemblyHeader()
488491
fieldCount = 0;
489492
foreach (var f in c.Fields.Where(f => !f.IsStatic && !f.IsLiteral))
490493
{
491-
// rename auto-properties backing field to a valid C++ identifier
492-
string fixedFieldName = string.Empty;
493-
string fieldWarning = string.Empty;
494-
495-
if (Regex.IsMatch(f.Name, @"<\w+>k__BackingField"))
496-
{
497-
fixedFieldName = $"{f.Name.Replace("<", "").Replace(">", "_")}";
498-
fieldWarning = $"// auto-property backing field renamed to '{fixedFieldName}'";
499-
}
494+
FixFieldName(f, out string fixedFieldName, out string fieldWarning);
500495

501496
if (_tablesContext.FieldsTable.TryGetFieldReferenceId(f, false, out ushort fieldRefId))
502497
{
@@ -566,6 +561,27 @@ private void GenerateAssemblyHeader()
566561
}
567562
}
568563

564+
/// <summary>
565+
/// Fix field name to a valid C++ identifier.
566+
/// </summary>
567+
/// <param name="field">The field definition to work on.</param>
568+
/// <param name="fixedFieldName">The fixed field name, or an <see cref="Empty"/> string if no fix is needed.</param>
569+
/// <param name="fieldWarning">The warning message to be added to the field declaration, or empty if no warning is needed.</param>
570+
private static void FixFieldName(
571+
FieldDefinition field,
572+
out string fixedFieldName,
573+
out string fieldWarning)
574+
{
575+
fixedFieldName = string.Empty;
576+
fieldWarning = string.Empty;
577+
578+
if (Regex.IsMatch(field.Name, @"<\w+>k__BackingField"))
579+
{
580+
fixedFieldName = $"{field.Name.Replace("<", "").Replace(">k__BackingField", "")}";
581+
fieldWarning = $"// renamed backing field '{field.Name}'";
582+
}
583+
}
584+
569585
private int GetInstanceFieldsOffset(TypeDefinition c)
570586
{
571587
// check if this type has a base type different from System.Object

MetadataProcessor.Tests/Core/StubsGenerationTests.cs

+81-25
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System;
77
using System.Collections.Generic;
88
using System.IO;
9+
using System.Text.RegularExpressions;
910
using Microsoft.VisualStudio.TestTools.UnitTesting;
1011
using Mono.Cecil;
1112
using nanoFramework.Tools.MetadataProcessor.Core;
@@ -58,15 +59,15 @@ public class StubsGenerationTests
5859
private const string NativeHeaderMethodGenerationDeclaration =
5960
"static void NativeMethodWithReferenceParameters( uint8_t& param0, uint16_t& param1, HRESULT &hr );";
6061

61-
private string stubPath;
62+
private string _stubsPath;
6263

6364
[TestMethod]
6465
public void GeneratingStubsFromNFAppTest()
6566
{
6667
// read generated stub file and look for the function declaration
6768
var generatedFile =
6869
File.ReadAllText(
69-
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.cpp");
70+
$"{_stubsPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.cpp");
7071

7172
Assert.IsTrue(generatedFile.Contains(NativeMethodGenerationDeclaration));
7273
}
@@ -76,7 +77,7 @@ public void GeneratingMarshallingStubsFromNFAppTest()
7677
{
7778
var generatedFile =
7879
File.ReadAllText(
79-
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration_mshl.cpp");
80+
$"{_stubsPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration_mshl.cpp");
8081

8182
Assert.IsTrue(generatedFile.Contains(NativeMarshallingMethodGenerationDeclaration));
8283
}
@@ -86,7 +87,7 @@ public void GeneratingHeaderStubsFromNFAppTest()
8687
{
8788
var generatedFile =
8889
File.ReadAllText(
89-
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.h");
90+
$"{_stubsPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.h");
9091

9192
Assert.IsTrue(generatedFile.Contains(NativeHeaderMethodGenerationDeclaration));
9293
}
@@ -127,15 +128,15 @@ public void GeneratingStaticMethodWithoutParams()
127128
{
128129
var generatedHeaderFile =
129130
File.ReadAllText(
130-
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.h");
131+
$"{_stubsPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.h");
131132

132133
var generatedMarshallFile =
133134
File.ReadAllText(
134-
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration_mshl.cpp");
135+
$"{_stubsPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration_mshl.cpp");
135136

136137
var generatedImplementationFile =
137138
File.ReadAllText(
138-
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.cpp");
139+
$"{_stubsPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.cpp");
139140

140141
Assert.IsTrue(generatedHeaderFile.Contains(StaticMethodWithoutParameterHeaderGeneration));
141142
Assert.IsTrue(generatedMarshallFile.Contains(StaticMethodWithoutParameterMarshallGeneration));
@@ -182,27 +183,41 @@ public void GeneratingStaticMethod()
182183
{
183184
var generatedHeaderFile =
184185
File.ReadAllText(
185-
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.h");
186+
$"{_stubsPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.h");
186187

187188
var generatedMarshallFile =
188189
File.ReadAllText(
189-
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration_mshl.cpp");
190+
$"{_stubsPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration_mshl.cpp");
190191

191192
var generatedImplementationFile =
192193
File.ReadAllText(
193-
$"{stubPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.cpp");
194+
$"{_stubsPath}\\StubsGenerationTestNFApp_StubsGenerationTestNFApp_NativeMethodGeneration.cpp");
194195

195196
Assert.IsTrue(generatedHeaderFile.Contains(StaticMethodHeaderGeneration));
196197
Assert.IsTrue(generatedMarshallFile.Contains(StaticMethodMarshallGeneration));
197198
Assert.IsTrue(generatedImplementationFile.Contains(StaticMethodImplementationGeneration));
198199
}
199200

201+
[TestMethod]
202+
public void BackingFieldsAbsentTests()
203+
{
204+
string generatedAssemblyHeaderFile =
205+
File.ReadAllText(
206+
$"{_stubsPath}\\TestNFClassLibrary.h");
207+
208+
// check for property with backing field patter in the name
209+
Assert.IsFalse(generatedAssemblyHeaderFile.Contains("k__BackingField ="), "Found a name with BackingField pattern, when it shouldn't");
210+
211+
// deep check for backing field name pattern (except for entry patter in comments)
212+
Assert.IsFalse(Regex.IsMatch(generatedAssemblyHeaderFile, @"(?<!')<\w+>k__BackingField(?!')"), "Found a name with BackingField pattern, when it shouldn't");
213+
}
214+
200215
[TestInitialize]
201216
public void GenerateStubs()
202217
{
203218
var loadHints = new Dictionary<string, string>(StringComparer.Ordinal)
204219
{
205-
["mscorlib"] = Path.Combine(Directory.GetParent(TestObjectHelper.GenerationNFAppFullPath).FullName,
220+
["mscorlib"] = Path.Combine(Directory.GetParent(TestObjectHelper.StubsGenerationNFAppFullPath).FullName,
206221
"mscorlib.dll")
207222
};
208223

@@ -212,51 +227,92 @@ public void GenerateStubs()
212227
"THIS_NAME_DOES_NOT_EXIST_IN_THE_PROJECT"
213228
};
214229

215-
var fileToParse = TestObjectHelper.GenerationNFAppFullPath;
216-
var fileToCompile = Path.ChangeExtension(fileToParse, "pe");
230+
// Conpile StubsGenerationNFApp
231+
string stubsGenerationFileToParse = TestObjectHelper.StubsGenerationNFAppFullPath;
232+
string stubsGenerationFileToCompile = Path.ChangeExtension(stubsGenerationFileToParse, "pe");
217233

218234
// get path where stubs will be generated
219-
stubPath = Path.Combine(
235+
_stubsPath = Path.Combine(
220236
TestObjectHelper.TestExecutionLocation,
221237
"Stubs");
222238

223-
var assemblyDefinition = AssemblyDefinition.ReadAssembly(
224-
fileToParse,
239+
AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(
240+
stubsGenerationFileToParse,
225241
new ReaderParameters { AssemblyResolver = new LoadHintsAssemblyResolver(loadHints) });
226242

227-
var assemblyBuilder = new nanoAssemblyBuilder(assemblyDefinition, classNamesToExclude, false);
243+
nanoAssemblyBuilder assemblyBuilder = new nanoAssemblyBuilder(assemblyDefinition, classNamesToExclude, false);
228244

229-
using (var stream = File.Open(
230-
Path.ChangeExtension(fileToCompile, "tmp"),
245+
using (FileStream stream = File.Open(
246+
Path.ChangeExtension(stubsGenerationFileToCompile, "tmp"),
231247
FileMode.Create,
232248
FileAccess.ReadWrite))
233-
using (var writer = new BinaryWriter(stream))
249+
using (BinaryWriter writer = new BinaryWriter(stream))
234250
{
235251
assemblyBuilder.Write(GetBinaryWriter(writer));
236252
}
237253

238254
// OK to delete tmp PE file
239-
File.Delete(Path.ChangeExtension(fileToCompile, "tmp"));
255+
File.Delete(Path.ChangeExtension(stubsGenerationFileToCompile, "tmp"));
240256

241257
assemblyBuilder.Minimize();
242258

243-
var tablesContext = assemblyBuilder.TablesContext;
259+
nanoTablesContext tablesContext = assemblyBuilder.TablesContext;
244260

245261
var skeletonGenerator = new nanoSkeletonGenerator(
246262
tablesContext,
247-
stubPath,
263+
_stubsPath,
248264
"testStubs",
249265
"StubsGenerationTestNFApp",
250266
false,
251267
false);
252268

253269
skeletonGenerator.GenerateSkeleton();
270+
271+
// Compile the TestNFClassLibrary
272+
string nfLibFileToParse = TestObjectHelper.TestNFClassLibFullPath;
273+
string nfLibFileToCompile = Path.ChangeExtension(nfLibFileToParse, "pe");
274+
275+
assemblyDefinition = AssemblyDefinition.ReadAssembly(
276+
nfLibFileToParse,
277+
new ReaderParameters { AssemblyResolver = new LoadHintsAssemblyResolver(loadHints) });
278+
279+
assemblyBuilder = new nanoAssemblyBuilder(
280+
assemblyDefinition,
281+
new List<string>(),
282+
false);
283+
284+
using (FileStream stream = File.Open(
285+
Path.ChangeExtension(nfLibFileToCompile, "tmp"),
286+
FileMode.Create,
287+
FileAccess.ReadWrite))
288+
289+
using (BinaryWriter writer = new BinaryWriter(stream))
290+
{
291+
assemblyBuilder.Write(GetBinaryWriter(writer));
292+
}
293+
294+
// OK to delete tmp PE file
295+
File.Delete(Path.ChangeExtension(nfLibFileToCompile, "tmp"));
296+
297+
assemblyBuilder.Minimize();
298+
299+
tablesContext = assemblyBuilder.TablesContext;
300+
301+
skeletonGenerator = new nanoSkeletonGenerator(
302+
tablesContext,
303+
_stubsPath,
304+
"testStubs",
305+
"TestNFClassLibrary",
306+
true,
307+
true);
308+
309+
skeletonGenerator.GenerateSkeleton();
254310
}
255311

256312
[TestCleanup]
257313
public void DeleteStubs()
258314
{
259-
Directory.Delete(stubPath, true);
315+
Directory.Delete(_stubsPath, true);
260316
}
261317

262318
private nanoBinaryWriter GetBinaryWriter(
@@ -265,4 +321,4 @@ private nanoBinaryWriter GetBinaryWriter(
265321
return nanoBinaryWriter.CreateLittleEndianBinaryWriter(writer);
266322
}
267323
}
268-
}
324+
}

MetadataProcessor.Tests/Core/Utility/DumperTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ public void DumpAssemblyTest()
4343
Assert.IsTrue(dumpFileContent.Contains("TypeRefProps [01000001]: Scope: 23000001 'System.Diagnostics.DebuggableAttribute'"), "Wrong entry for System.Diagnostics.DebuggableAttribute in type ref");
4444
Assert.IsTrue(dumpFileContent.Contains(": Scope: 23000002 'TestNFClassLibrary.ClassOnAnotherAssembly'"), "Wrong entry for TestNFClassLibrary.ClassOnAnotherAssembly in type ref");
4545

46-
Assert.IsTrue(dumpFileContent.Contains(": Flags: 00001001 Extends: 0100000f Enclosed: 02000000 'TestNFApp.DummyCustomAttribute1'"), "Wrong entry for TestNFApp.DummyCustomAttribute1 in type ref");
46+
Assert.IsTrue(dumpFileContent.Contains(": Flags: 00001001 Extends: 01000010 Enclosed: 02000000 'TestNFApp.DummyCustomAttribute1'"), "Wrong entry for TestNFApp.DummyCustomAttribute1 in type ref");
4747

48-
Assert.IsTrue(dumpFileContent.Contains(": Flags: 00001001 Extends: 0100000f Enclosed: 02000000 'TestNFApp.DummyCustomAttribute2'"), "Wrong entry for TestNFApp.DummyCustomAttribute2 in type ref");
48+
Assert.IsTrue(dumpFileContent.Contains(": Flags: 00001001 Extends: 01000010 Enclosed: 02000000 'TestNFApp.DummyCustomAttribute2'"), "Wrong entry for TestNFApp.DummyCustomAttribute2 in type ref");
4949

5050
Assert.IsTrue(dumpFileContent.Contains(": Flags: 00001061 Extends: 01000000 Enclosed: 02000000 'TestNFApp.IOneClassOverAll'"), "Wrong entry for TestNFApp.IOneClassOverAll in type ref");
5151
Assert.IsTrue(dumpFileContent.Contains(": Flags: 000007c6 Impl: 00000000 RVA: 00000000 'get_DummyProperty' [I4( )]"), "Wrong entry for get_DummyProperty in type ref");

MetadataProcessor.Tests/MetadataProcessor.Tests.csproj

+3-1
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,11 @@
129129
"$(MSBuildBinPath)\msbuild" "$(ProjectDir)TestNFApp\TestNFApp.nfproj" -t:Build -nr:False -p:Configuration=$(Configuration) -p:NF_MDP_MSBUILDTASK_PATH="$(ProjectDir)..\MetadataProcessor.MsBuildTask\bin\$(Configuration)\net472"
130130
"$(MSBuildBinPath)\msbuild" "$(ProjectDir)StubsGenerationTestNFApp\StubsGenerationTestNFApp.nfproj" -t:Build -nr:False -p:Configuration=$(Configuration) -p:NF_MDP_MSBUILDTASK_PATH="$(ProjectDir)..\MetadataProcessor.MsBuildTask\bin\$(Configuration)\net472"
131131
mkdir "$(TargetDir)\TestNFApp"
132+
mkdir "$(TargetDir)\TestNFClassLibrary"
132133
mkdir "$(TargetDir)\StubsGenerationTestNFApp"
133134
copy /y "$(ProjectDir)TestNFApp\$(OutDir)\*" "$(TargetDir)\TestNFApp"
135+
copy /y "$(ProjectDir)TestNFApp\$(OutDir)\TestNFClassLibrary.*" "$(TargetDir)\TestNFClassLibrary"
134136
copy /y "$(ProjectDir)StubsGenerationTestNFApp\$(OutDir)\*" "$(TargetDir)\StubsGenerationTestNFApp"
135137
</PreBuildEvent>
136138
</PropertyGroup>
137-
</Project>
139+
</Project>

MetadataProcessor.Tests/TestNFApp/Properties/AssemblyInfo.cs

+6
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,9 @@
3131
// [assembly: AssemblyVersion("1.0.*")]
3232
[assembly: AssemblyVersion("1.0.0.0")]
3333
[assembly: AssemblyFileVersion("1.0.0.0")]
34+
35+
/////////////////////////////////////////////////////////////////
36+
// This attribute is mandatory when building Interop libraries //
37+
// update this whenever the native assembly signature changes //
38+
[assembly: AssemblyNativeVersion("0.0.0.0")]
39+
/////////////////////////////////////////////////////////////////
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// See LICENSE file in the project root for full license information.
4+
//
5+
6+
using System.Runtime.CompilerServices;
7+
8+
namespace TestNFClassLibrary
9+
{
10+
public class ClassWithNativeImplementation
11+
{
12+
private static int _staticField;
13+
private int _field;
14+
15+
public static int StaticProperty1 { get; set; }
16+
17+
public int Property1 { get; set; }
18+
19+
public void ManagedMethod1()
20+
{
21+
NativeMethod1();
22+
}
23+
24+
public void ManagedMethod2()
25+
{
26+
NativeMethod2();
27+
}
28+
29+
[MethodImpl(MethodImplOptions.InternalCall)]
30+
public static extern void NativeMethod1();
31+
32+
[MethodImpl(MethodImplOptions.InternalCall)]
33+
public static extern void NativeMethod2();
34+
}
35+
}

MetadataProcessor.Tests/TestNFClassLibrary/TestNFClassLibrary/TestNFClassLibrary.nfproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<ItemGroup>
2121
<Compile Include="IAmAClassWithAnEnum.cs" />
2222
<Compile Include="ClassOnAnotherAssembly.cs" />
23+
<Compile Include="ClassWithNativeImplementation.cs" />
2324
<Compile Include="IAmAnEnumToExclude.cs" />
2425
<Compile Include="IAmAlsoATypeToExclude.cs" />
2526
<Compile Include="IAmATypeToExclude.cs" />
@@ -39,4 +40,4 @@
3940
<ProjectConfigurationsDeclaredAsItems />
4041
</ProjectCapabilities>
4142
</ProjectExtensions>
42-
</Project>
43+
</Project>

0 commit comments

Comments
 (0)