Skip to content

Commit 107d708

Browse files
authored
Improve type comparison for declarations and stubs (#181)
***NO_CI***
1 parent 8281370 commit 107d708

File tree

2 files changed

+123
-11
lines changed

2 files changed

+123
-11
lines changed

MetadataProcessor.Shared/nanoSkeletonGenerator.cs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public sealed class nanoSkeletonGenerator
3030

3131
private string _safeProjectName => _project.Replace('.', '_');
3232

33+
public string SafeProjectName => _safeProjectName;
34+
3335
public nanoSkeletonGenerator(
3436
nanoTablesContext tablesContext,
3537
string path,
@@ -87,11 +89,9 @@ private void GenerateStubs()
8789
IsInterop = !_withoutInteropCode
8890
};
8991

90-
foreach (var c in _tablesContext.TypeDefinitionTable.TypeDefinitions)
92+
foreach (TypeDefinition c in _tablesContext.TypeDefinitionTable.TypeDefinitions)
9193
{
92-
if (c.IncludeInStub() &&
93-
!c.IsToExclude() &&
94-
!nanoTablesContext.IgnoringAttributes.Contains(c.FullName))
94+
if (ShouldIncludeType(c))
9595
{
9696
var className = NativeMethodsCrc.GetClassName(c);
9797

@@ -370,12 +370,12 @@ private void GenerateAssemblyLookup()
370370
};
371371

372372

373-
foreach (var c in _tablesContext.TypeDefinitionTable.Items)
373+
foreach (TypeDefinition c in _tablesContext.TypeDefinitionTable.Items)
374374
{
375375
// only care about types that have methods
376376
if (c.HasMethods)
377377
{
378-
if (c.IncludeInStub())
378+
if (ShouldIncludeType(c))
379379
{
380380
var className = NativeMethodsCrc.GetClassName(c);
381381

@@ -399,8 +399,11 @@ private void GenerateAssemblyLookup()
399399
// need to add a NULL entry for it
400400
assemblyLookup.LookupTable.Add(new MethodStub()
401401
{
402+
#if DEBUG
403+
Declaration = $"NULL, // <<<<< Library_{_safeProjectName}_{NativeMethodsCrc.GetClassName(c)}::{NativeMethodsCrc.GetMethodName(m)}",
404+
#else
402405
Declaration = "NULL"
403-
//Declaration = $"**Library_{_safeProjectName}_{NativeMethodsCrc.GetClassName(c)}::{NativeMethodsCrc.GetMethodName(m)}"
406+
#endif
404407
});
405408
}
406409
}
@@ -413,8 +416,11 @@ private void GenerateAssemblyLookup()
413416
{
414417
assemblyLookup.LookupTable.Add(new MethodStub()
415418
{
419+
#if DEBUG
420+
Declaration = $"NULL, // <<<<< Library_{_safeProjectName}_{NativeMethodsCrc.GetClassName(c)}::{NativeMethodsCrc.GetMethodName(m)}",
421+
#else
416422
Declaration = "NULL"
417-
//Declaration = $"**Library_{_safeProjectName}_{NativeMethodsCrc.GetClassName(c)}::{NativeMethodsCrc.GetMethodName(m)}"
423+
#endif
418424
});
419425
}
420426
}
@@ -443,10 +449,9 @@ private void GenerateAssemblyHeader()
443449
IsCoreLib = _isCoreLib
444450
};
445451

446-
foreach (var c in _tablesContext.TypeDefinitionTable.Items)
452+
foreach (TypeDefinition c in _tablesContext.TypeDefinitionTable.Items)
447453
{
448-
if (c.IncludeInStub() &&
449-
!c.IsToExclude())
454+
if (ShouldIncludeType(c))
450455
{
451456
var classData = new Class()
452457
{
@@ -619,5 +624,12 @@ private int GetNestedFieldsCount(TypeDefinition c)
619624
return c.Fields.Count(f => !f.IsStatic && !f.IsLiteral);
620625
}
621626
}
627+
628+
internal static bool ShouldIncludeType(TypeDefinition type)
629+
{
630+
return type.IncludeInStub()
631+
&& !type.IsToExclude()
632+
&& !nanoTablesContext.IgnoringAttributes.Contains(type.FullName);
633+
}
622634
}
623635
}

MetadataProcessor.Tests/Core/StubsGenerationTests.cs

Lines changed: 100 additions & 0 deletions
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.Linq;
910
using System.Text.RegularExpressions;
1011
using Microsoft.VisualStudio.TestTools.UnitTesting;
1112
using Mono.Cecil;
@@ -60,6 +61,8 @@ public class StubsGenerationTests
6061
"static void NativeMethodWithReferenceParameters( uint8_t& param0, uint16_t& param1, HRESULT &hr );";
6162

6263
private string _stubsPath;
64+
private List<string> _nfTestLibTypeToIncludeInLookupTable = new List<string>();
65+
private List<string> _nfTestLibTypeToIncludeInHeader = new List<string>();
6366

6467
[TestMethod]
6568
public void GeneratingStubsFromNFAppTest()
@@ -212,6 +215,49 @@ public void BackingFieldsAbsentTests()
212215
Assert.IsFalse(Regex.IsMatch(generatedAssemblyHeaderFile, @"(?<!')<\w+>k__BackingField(?!')"), "Found a name with BackingField pattern, when it shouldn't");
213216
}
214217

218+
[TestMethod]
219+
public void StubsAndDeclarationMatchTests()
220+
{
221+
string generatedAssemblyHeaderFile = File.ReadAllText($"{_stubsPath}\\TestNFClassLibrary.h");
222+
string generatedAssemblyLookupFile = File.ReadAllText($"{_stubsPath}\\TestNFClassLibrary.cpp");
223+
224+
// extract all type definitions from the header file
225+
MatchCollection typeDefinitionsInHeader = Regex.Matches(generatedAssemblyHeaderFile, @"struct\s{1}(\w+_\w+_\w+_\w+)\b", RegexOptions.IgnoreCase | RegexOptions.Multiline);
226+
227+
// extract all type definitions from the lookup file
228+
List<Match> typeDefinitionsInLookupTable = Regex.Matches(generatedAssemblyLookupFile, @"^\s{4}(\w+_\w+_\w+_\w+)::", RegexOptions.IgnoreCase | RegexOptions.Multiline)
229+
.Cast<Match>()
230+
.GroupBy(m => m.Groups[1].Value)
231+
.Select(g => g.First())
232+
.ToList();
233+
234+
// check if all entries in lookup table are present in the header
235+
foreach (Match typeDefinition in typeDefinitionsInLookupTable)
236+
{
237+
string typeName = typeDefinition.Groups[1].Value;
238+
bool found = typeDefinitionsInHeader.Cast<Match>().Any(md => md.Groups[1].Value == typeName);
239+
Assert.IsTrue(found, $"Type definition {typeName} not found in header file");
240+
}
241+
242+
// check if all expected types are present in the lookup table
243+
Assert.AreEqual(_nfTestLibTypeToIncludeInLookupTable.Count, typeDefinitionsInLookupTable.Count, "Number of type definitions don't match");
244+
245+
foreach (string typeName in _nfTestLibTypeToIncludeInLookupTable)
246+
{
247+
bool found = typeDefinitionsInLookupTable.Any(md => md.Groups[1].Value == typeName);
248+
Assert.IsTrue(found, $"Type definition {typeName} not found in lookup table");
249+
}
250+
251+
// check if all expected types are present in the header file
252+
Assert.AreEqual(_nfTestLibTypeToIncludeInHeader.Count, typeDefinitionsInHeader.Count, "Number of type definitions don't match");
253+
254+
foreach (string typeName in _nfTestLibTypeToIncludeInHeader)
255+
{
256+
bool found = typeDefinitionsInHeader.Cast<Match>().Any(md => md.Groups[1].Value == typeName);
257+
Assert.IsTrue(found, $"Type definition {typeName} not found in header file");
258+
}
259+
}
260+
215261
[TestInitialize]
216262
public void GenerateStubs()
217263
{
@@ -256,6 +302,16 @@ public void GenerateStubs()
256302

257303
assemblyBuilder.Minimize();
258304

305+
// recompile
306+
using (FileStream stream = File.Open(
307+
Path.ChangeExtension(stubsGenerationFileToCompile, "tmp"),
308+
FileMode.Create,
309+
FileAccess.ReadWrite))
310+
using (BinaryWriter writer = new BinaryWriter(stream))
311+
{
312+
assemblyBuilder.Write(GetBinaryWriter(writer));
313+
}
314+
259315
nanoTablesContext tablesContext = assemblyBuilder.TablesContext;
260316

261317
var skeletonGenerator = new nanoSkeletonGenerator(
@@ -296,6 +352,17 @@ public void GenerateStubs()
296352

297353
assemblyBuilder.Minimize();
298354

355+
// recompile
356+
using (FileStream stream = File.Open(
357+
Path.ChangeExtension(nfLibFileToCompile, "tmp"),
358+
FileMode.Create,
359+
FileAccess.ReadWrite))
360+
361+
using (BinaryWriter writer = new BinaryWriter(stream))
362+
{
363+
assemblyBuilder.Write(GetBinaryWriter(writer));
364+
}
365+
299366
tablesContext = assemblyBuilder.TablesContext;
300367

301368
skeletonGenerator = new nanoSkeletonGenerator(
@@ -307,6 +374,39 @@ public void GenerateStubs()
307374
true);
308375

309376
skeletonGenerator.GenerateSkeleton();
377+
378+
// save types that are to be included from assembly lookup declaration
379+
foreach (TypeDefinition c in tablesContext.TypeDefinitionTable.Items)
380+
{
381+
if (c.HasMethods && nanoSkeletonGenerator.ShouldIncludeType(c))
382+
{
383+
foreach (MethodDefinition m in nanoTablesContext.GetOrderedMethods(c.Methods))
384+
{
385+
ushort rva = tablesContext.ByteCodeTable.GetMethodRva(m);
386+
387+
// check method inclusion
388+
// method is not a native implementation (RVA 0xFFFF) and is not abstract
389+
if (rva == 0xFFFF && !m.IsAbstract)
390+
{
391+
_nfTestLibTypeToIncludeInLookupTable.Add($"Library_{skeletonGenerator.SafeProjectName}_{NativeMethodsCrc.GetClassName(c)}");
392+
393+
// only need to add the type once
394+
break;
395+
}
396+
}
397+
}
398+
}
399+
400+
// save types that are to be included in assembly header
401+
foreach (TypeDefinition c in tablesContext.TypeDefinitionTable.Items)
402+
{
403+
if (nanoSkeletonGenerator.ShouldIncludeType(c)
404+
&& c.HasMethods
405+
&& c.HasFields)
406+
{
407+
_nfTestLibTypeToIncludeInHeader.Add($"Library_{skeletonGenerator.SafeProjectName}_{NativeMethodsCrc.GetClassName(c)}");
408+
}
409+
}
310410
}
311411

312412
[TestCleanup]

0 commit comments

Comments
 (0)