Skip to content

Commit 89fca38

Browse files
authored
Fix stubs generation to support generics notation (#199)
***NO_CI***
1 parent 4ef7681 commit 89fca38

File tree

7 files changed

+129
-45
lines changed

7 files changed

+129
-45
lines changed

MetadataProcessor.Shared/Utility/NativeMethodsCrc.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System;
77
using System.Collections.Generic;
88
using System.Text;
9+
using System.Text.RegularExpressions;
910
using Mono.Cecil;
1011
using nanoFramework.Tools.MetadataProcessor.Core.Extensions;
1112

@@ -62,8 +63,8 @@ public void UpdateCrc(MethodDefinition method)
6263
(method.RVA == 0 && !method.IsAbstract))
6364
{
6465
_currentCrc = Crc32.Compute(_name, CurrentCrc);
65-
_currentCrc = Crc32.Compute(Encoding.ASCII.GetBytes(GetClassName(type)), CurrentCrc);
66-
_currentCrc = Crc32.Compute(Encoding.ASCII.GetBytes(GetMethodName(method)), CurrentCrc);
66+
_currentCrc = Crc32.Compute(Encoding.ASCII.GetBytes(GetSafeClassName(type)), CurrentCrc);
67+
_currentCrc = Crc32.Compute(Encoding.ASCII.GetBytes(GetSafeMethodName(method)), CurrentCrc);
6768

6869
_methodsWithNativeImplementation++;
6970
}
@@ -73,25 +74,26 @@ public void UpdateCrc(MethodDefinition method)
7374
}
7475
}
7576

76-
internal static string GetClassName(
77-
TypeDefinition type)
77+
internal static string GetSafeClassName(TypeDefinition type)
7878
{
79-
return (type != null
80-
? string.Join("_", GetClassName(type.DeclaringType), type.Namespace, type.Name)
81-
.Replace(".", "_").TrimStart('_')
79+
string className = (type != null
80+
? string.Join("_", GetSafeClassName(type.DeclaringType), type.Namespace, type.Name)
81+
.Replace(".", "_")
82+
.TrimStart('_')
8283
: string.Empty);
84+
85+
return CleanupGenericName(className);
8386
}
8487

85-
internal static string GetMethodName(
86-
MethodDefinition method)
88+
internal static string GetSafeMethodName(MethodDefinition method)
8789
{
88-
var name = string.Concat(method.Name, (method.IsStatic ? "___STATIC__" : "___"),
90+
string name = string.Concat(method.Name, (method.IsStatic ? "___STATIC__" : "___"),
8991
string.Join("__", GetAllParameters(method)));
9092

91-
var originalName = name.Replace(".", "_")
93+
string originalName = name.Replace(".", "_")
9294
.Replace("/", "");
9395

94-
return originalName;
96+
return CleanupGenericName(originalName);
9597
}
9698

9799
private static IEnumerable<string> GetAllParameters(
@@ -192,7 +194,9 @@ internal static string GetNanoCLRTypeName(TypeReference parameterType)
192194
else
193195
{
194196
// this is not a generic, get full qualified type name
195-
return parameterType.FullName.Replace(".", String.Empty);
197+
string typeName = parameterType.FullName.Replace(".", String.Empty);
198+
199+
return CleanupGenericName(typeName);
196200
}
197201
}
198202
}
@@ -216,5 +220,14 @@ private bool IsClassToExclude(TypeDefinition td)
216220
return (_classNamesToExclude.Contains(td.FullName) ||
217221
_classNamesToExclude.Contains(td.DeclaringType?.FullName));
218222
}
223+
224+
internal static string CleanupGenericName(string name)
225+
{
226+
// Remove generic type notation: anything like <T>, <T1,T2>, `, etc.
227+
string fixedName = name
228+
.Replace('`', '_');
229+
230+
return Regex.Replace(fixedName, @"<[^>]*>", string.Empty);
231+
}
219232
}
220233
}

MetadataProcessor.Shared/nanoSkeletonGenerator.cs

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,6 @@ public void GenerateSkeleton()
8383

8484
private void GenerateStubs()
8585
{
86-
var generatedFiles = new List<string>();
87-
8886
var classList = new AssemblyClassTable
8987
{
9088
AssemblyName = _tablesContext.AssemblyDefinition.Name.Name,
@@ -96,22 +94,22 @@ private void GenerateStubs()
9694
{
9795
if (ShouldIncludeType(c))
9896
{
99-
var className = NativeMethodsCrc.GetClassName(c);
97+
string _safeClassName = NativeMethodsCrc.GetSafeClassName(c);
10098

10199
var classStubs = new AssemblyClassStubs
102100
{
103101
AssemblyName = _name,
104-
ClassHeaderFileName = className,
105-
ClassName = c.Name,
106-
ShortNameUpper = $"{_assemblyName}_{_safeProjectName}_{className}".ToUpper(),
102+
ClassHeaderFileName = _safeClassName,
103+
ClassName = NativeMethodsCrc.CleanupGenericName(c.Name),
104+
ShortNameUpper = $"{_assemblyName}_{_safeProjectName}_{_safeClassName}".ToUpper(),
107105
RootNamespace = _assemblyName,
108106
ProjectName = _safeProjectName,
109107
HeaderFileName = _safeProjectName
110108
};
111109

112110
classList.Classes.Add(new Class()
113111
{
114-
Name = className
112+
Name = _safeClassName
115113
});
116114
classList.HeaderFileName = classStubs.HeaderFileName;
117115

@@ -125,7 +123,7 @@ private void GenerateStubs()
125123
{
126124
var newMethod = new MethodStub()
127125
{
128-
Declaration = $"Library_{_safeProjectName}_{className}::{NativeMethodsCrc.GetMethodName(m)}"
126+
Declaration = $"Library_{_safeProjectName}_{_safeClassName}::{NativeMethodsCrc.GetSafeMethodName(m)}"
129127
};
130128

131129
if (!_withoutInteropCode)
@@ -143,10 +141,10 @@ private void GenerateStubs()
143141

144142
newMethod.MarshallingReturnType = m.MethodReturnType.ReturnType.ToCLRTypeAsString();
145143

146-
declaration.Append($"{m.Name}");
144+
declaration.Append($"{NativeMethodsCrc.CleanupGenericName(m.Name)}");
147145
declaration.Append("( ");
148146

149-
StringBuilder marshallingCall = new StringBuilder($"{m.Name}");
147+
StringBuilder marshallingCall = new StringBuilder($"{NativeMethodsCrc.CleanupGenericName(m.Name)}");
150148
marshallingCall.Append("( ");
151149

152150
// loop through the parameters
@@ -259,16 +257,16 @@ private void GenerateStubs()
259257
};
260258
Generator generator = compiler.Compile(SkeletonTemplates.ClassWithoutInteropStubTemplate);
261259

262-
using (var headerFile = File.CreateText(Path.Combine(_path, $"{_safeProjectName}_{className}.cpp")))
260+
using (StreamWriter headerFile = File.CreateText(Path.Combine(_path, $"{_safeProjectName}_{_safeClassName}.cpp")))
263261
{
264-
var output = generator.Render(classStubs);
262+
string output = generator.Render(classStubs);
265263
headerFile.Write(output);
266264
}
267265

268266
// add class to list of classes with stubs
269267
classList.ClassesWithStubs.Add(new ClassWithStubs()
270268
{
271-
Name = className
269+
Name = _safeClassName
272270
});
273271
}
274272
else
@@ -281,34 +279,34 @@ private void GenerateStubs()
281279
// user code stub
282280
Generator generator = compiler.Compile(SkeletonTemplates.ClassStubTemplate);
283281

284-
using (var headerFile = File.CreateText(Path.Combine(_path, $"{_safeProjectName}_{className}.cpp")))
282+
using (StreamWriter headerFile = File.CreateText(Path.Combine(_path, $"{_safeProjectName}_{_safeClassName}.cpp")))
285283
{
286-
var output = generator.Render(classStubs);
284+
string output = generator.Render(classStubs);
287285
headerFile.Write(output);
288286
}
289287

290288
// marshal code
291289
generator = compiler.Compile(SkeletonTemplates.ClassMarshallingCodeTemplate);
292290

293-
using (var headerFile = File.CreateText(Path.Combine(_path, $"{_safeProjectName}_{className}_mshl.cpp")))
291+
using (StreamWriter headerFile = File.CreateText(Path.Combine(_path, $"{_safeProjectName}_{_safeClassName}_mshl.cpp")))
294292
{
295-
var output = generator.Render(classStubs);
293+
string output = generator.Render(classStubs);
296294
headerFile.Write(output);
297295
}
298296

299297
// class header
300298
generator = compiler.Compile(SkeletonTemplates.ClassHeaderTemplate);
301299

302-
using (var headerFile = File.CreateText(Path.Combine(_path, $"{_safeProjectName}_{className}.h")))
300+
using (StreamWriter headerFile = File.CreateText(Path.Combine(_path, $"{_safeProjectName}_{_safeClassName}.h")))
303301
{
304-
var output = generator.Render(classStubs);
302+
string output = generator.Render(classStubs);
305303
headerFile.Write(output);
306304
}
307305

308306
// add class to list of classes with stubs
309307
classList.ClassesWithStubs.Add(new ClassWithStubs()
310308
{
311-
Name = className
309+
Name = _safeClassName
312310
});
313311
}
314312
}
@@ -379,7 +377,7 @@ private void GenerateAssemblyLookup()
379377
{
380378
if (ShouldIncludeType(c))
381379
{
382-
var className = NativeMethodsCrc.GetClassName(c);
380+
var className = NativeMethodsCrc.GetSafeClassName(c);
383381

384382
foreach (var m in nanoTablesContext.GetOrderedMethods(c.Methods))
385383
{
@@ -392,7 +390,7 @@ private void GenerateAssemblyLookup()
392390
{
393391
assemblyLookup.LookupTable.Add(new MethodStub()
394392
{
395-
Declaration = $"Library_{_safeProjectName}_{className}::{NativeMethodsCrc.GetMethodName(m)}"
393+
Declaration = $"Library_{_safeProjectName}_{className}::{NativeMethodsCrc.GetSafeMethodName(m)}"
396394
});
397395
}
398396
else
@@ -458,7 +456,7 @@ private void GenerateAssemblyHeader()
458456
var classData = new Class()
459457
{
460458
AssemblyName = _safeProjectName,
461-
Name = NativeMethodsCrc.GetClassName(c)
459+
Name = NativeMethodsCrc.GetSafeClassName(c)
462460
};
463461

464462
// If class name starts from <PrivateImplementationDetails>,
@@ -525,7 +523,7 @@ private void GenerateAssemblyHeader()
525523
{
526524
classData.Methods.Add(new MethodStub()
527525
{
528-
Declaration = NativeMethodsCrc.GetMethodName(m)
526+
Declaration = NativeMethodsCrc.GetSafeMethodName(m)
529527
});
530528
}
531529
}

MetadataProcessor.Tests/Core/Utility/NativeMethodsCrcTests.cs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,30 @@ public void GetClassNameTest()
1818
var typeDefinition = TestObjectHelper.GetTestNFAppOneClassOverAllTypeDefinition(nanoTablesContext.AssemblyDefinition);
1919

2020
// test
21-
var r = NativeMethodsCrc.GetClassName(typeDefinition);
21+
var r = NativeMethodsCrc.GetSafeClassName(typeDefinition);
2222

2323
Assert.AreEqual("TestNFApp_OneClassOverAll", r);
2424

2525
// test
26-
r = NativeMethodsCrc.GetClassName(null);
26+
r = NativeMethodsCrc.GetSafeClassName(null);
2727

2828
Assert.AreEqual(String.Empty, r);
2929
}
3030

31+
[TestMethod]
32+
public void GetClassNameWithGenericsTest()
33+
{
34+
nanoTablesContext nanoTablesContext = TestObjectHelper.GetTestNFAppNanoTablesContext();
35+
TypeDefinition genericTypeDefinition = TestObjectHelper.GetTestNFAppGenericClassTypeDefinition(nanoTablesContext.AssemblyDefinition);
36+
TypeDefinition anotherGenericTypeDefinition = TestObjectHelper.GetTestNFAppAnotherGenericClassTypeDefinition(nanoTablesContext.AssemblyDefinition);
37+
38+
// test
39+
Assert.AreEqual("TestNFApp_GenericClass_1", NativeMethodsCrc.GetSafeClassName(genericTypeDefinition));
40+
Assert.AreEqual("TestNFApp_AnotherGenericClass_2", NativeMethodsCrc.GetSafeClassName(anotherGenericTypeDefinition));
41+
42+
Assert.AreEqual(String.Empty, NativeMethodsCrc.GetSafeClassName(null));
43+
}
44+
3145
[TestMethod]
3246
public void GetMethodNameTest()
3347
{
@@ -36,7 +50,7 @@ public void GetMethodNameTest()
3650
var methodDefinition = TestObjectHelper.GetTestNFAppOneClassOverAllDummyMethodDefinition(typeDefinition);
3751

3852
// test
39-
var r = NativeMethodsCrc.GetMethodName(methodDefinition);
53+
var r = NativeMethodsCrc.GetSafeMethodName(methodDefinition);
4054

4155
Assert.AreEqual("DummyMethod___VOID", r);
4256

@@ -45,7 +59,7 @@ public void GetMethodNameTest()
4559
methodDefinition = TestObjectHelper.GetTestNFAppOneClassOverAllDummyMethodWithParamsDefinition(typeDefinition);
4660

4761
// test
48-
r = NativeMethodsCrc.GetMethodName(methodDefinition);
62+
r = NativeMethodsCrc.GetSafeMethodName(methodDefinition);
4963

5064
Assert.AreEqual("DummyMethodWithParams___VOID__I4__STRING", r);
5165

@@ -54,7 +68,7 @@ public void GetMethodNameTest()
5468
methodDefinition = TestObjectHelper.GetTestNFAppOneClassOverAllDummyStaticMethodDefinition(typeDefinition);
5569

5670
// test
57-
r = NativeMethodsCrc.GetMethodName(methodDefinition);
71+
r = NativeMethodsCrc.GetSafeMethodName(methodDefinition);
5872

5973
Assert.AreEqual("DummyStaticMethod___STATIC__VOID", r);
6074

@@ -63,7 +77,7 @@ public void GetMethodNameTest()
6377
methodDefinition = TestObjectHelper.GetTestNFAppOneClassOverAllDummyStaticMethodWithParamsDefinition(typeDefinition);
6478

6579
// test
66-
r = NativeMethodsCrc.GetMethodName(methodDefinition);
80+
r = NativeMethodsCrc.GetSafeMethodName(methodDefinition);
6781

6882
Assert.AreEqual("DummyStaticMethodWithParams___STATIC__VOID__I8__SystemDateTime", r);
6983

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.CompilerServices;
5+
6+
namespace StubsGenerationTestNFApp
7+
{
8+
internal class NativeMethodGenerationGenerics<T>
9+
{
10+
public void Method<U>()
11+
{
12+
NativeMethod();
13+
14+
byte a = 0;
15+
ushort b = 0;
16+
U genParam = default;
17+
18+
NativeMethodWithReferenceParameters<T, U>(ref a, ref b);
19+
20+
NativeStaticMethod<U>(default);
21+
}
22+
23+
[MethodImpl(MethodImplOptions.InternalCall)]
24+
private extern void NativeMethod();
25+
26+
[MethodImpl(MethodImplOptions.InternalCall)]
27+
private extern void NativeMethodWithReferenceParameters<U, V>(ref byte refByteParam, ref ushort refUshortParam);
28+
29+
[MethodImpl(MethodImplOptions.InternalCall)]
30+
private static extern void NativeStaticMethod<U>(T genParam);
31+
32+
[MethodImpl(MethodImplOptions.InternalCall)]
33+
private static extern T NativeStaticMethodReturningType(char charParam);
34+
}
35+
}

MetadataProcessor.Tests/StubsGenerationTestNFApp/Program.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ public class Program
99
{
1010
public static void Main()
1111
{
12-
var nativeMethods = new NativeMethodGeneration();
12+
NativeMethodGeneration nativeMethods = new NativeMethodGeneration();
1313
nativeMethods.Method();
1414

15+
NativeMethodGenerationGenerics<int> nativeMethodGenerationGenerics = new NativeMethodGenerationGenerics<int>();
16+
17+
nativeMethodGenerationGenerics.Method<int>();
18+
1519
Thread.Sleep(Timeout.Infinite);
1620
}
1721
}
18-
}
22+
}

MetadataProcessor.Tests/StubsGenerationTestNFApp/StubsGenerationTestNFApp.nfproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616
<AssemblyName>StubsGenerationTestNFApp</AssemblyName>
1717
<TargetFrameworkVersion>v1.0</TargetFrameworkVersion>
1818
</PropertyGroup>
19+
<PropertyGroup Label="nanoFramework">
20+
<NFMDP_GENERATE_STUBS>True</NFMDP_GENERATE_STUBS>
21+
</PropertyGroup>
1922
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
2023
<ItemGroup>
24+
<Compile Include="NativeMethodGenerationGenerics.cs" />
2125
<Compile Include="NativeMethodGeneration.cs" />
2226
<Compile Include="Program.cs" />
2327
<Compile Include="Properties\AssemblyInfo.cs" />

MetadataProcessor.Tests/TestObjectHelper.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,22 @@ public static TypeDefinition GetTestNFAppOneClassOverAllSubClassTypeDefinition(A
276276
return ret;
277277
}
278278

279+
public static TypeDefinition GetTestNFAppGenericClassTypeDefinition(AssemblyDefinition assemblyDefinition)
280+
{
281+
TypeDefinition ret = null;
282+
ModuleDefinition module = assemblyDefinition.Modules[0];
283+
ret = module.Types.First(i => i.FullName == "TestNFApp.GenericClass`1");
284+
return ret;
285+
}
286+
287+
public static TypeDefinition GetTestNFAppAnotherGenericClassTypeDefinition(AssemblyDefinition assemblyDefinition)
288+
{
289+
TypeDefinition ret = null;
290+
ModuleDefinition module = assemblyDefinition.Modules[0];
291+
ret = module.Types.First(i => i.FullName == "TestNFApp.AnotherGenericClass`2");
292+
return ret;
293+
}
294+
279295
public static MethodDefinition GetTestNFAppOneClassOverAllDummyMethodDefinition(TypeDefinition oneClassOverAllTypeDefinition)
280296
{
281297
MethodDefinition ret = GetTestNFAppOneClassOverAllMethodDefinition(oneClassOverAllTypeDefinition, "DummyMethod");

0 commit comments

Comments
 (0)