Skip to content

Commit ffc10a0

Browse files
authored
Work on adding support for generics (#124)
1 parent 6f5a569 commit ffc10a0

13 files changed

+237
-10
lines changed

MetadataProcessor.Core/DumpGenerator/DumpTemplates.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ internal partial class DumpTemplates
2323
2424
{{#each TypeDefinitions}}
2525
TypeDefProps [{{ReferenceId}}]: Flags: {{Flags}} Extends: {{ExtendsType}} Enclosed: {{EnclosedType}} '{{Name}}'{{#newline}}
26+
{{#each GenericParameters}}
27+
GenericParam [{{GenericParamToken}}]: Position: ({{Position}}) '{{Name}}' Owner: {{Owner}} [{{Signature}}]{{#newline}}
28+
{{/each}}
29+
2630
{{#each FieldDefinitions}}
2731
FieldDefProps [{{ReferenceId}}]: Attr: {{Attributes}} Flags: {{Flags}} '{{Name}}' [{{Signature}}]{{#newline}}
2832
{{/each}}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// See LICENSE file in the project root for full license information.
4+
//
5+
6+
namespace nanoFramework.Tools.MetadataProcessor.Core
7+
{
8+
public class GenericParam
9+
{
10+
public string Position;
11+
12+
public string GenericParamToken;
13+
14+
public string Name;
15+
16+
public string Owner;
17+
18+
public string Signature;
19+
}
20+
}

MetadataProcessor.Core/DumpGenerator/TypeDef.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class TypeDef
1919

2020
public string Name;
2121

22+
public List<GenericParam> GenericParameters = new List<GenericParam>();
2223
public List<FieldDef> FieldDefinitions = new List<FieldDef>();
2324
public List<MethodDef> MethodDefinitions = new List<MethodDef>();
2425
public List<InterfaceDef> InterfaceDefinitions = new List<InterfaceDef>();

MetadataProcessor.Core/Extensions/TypeReferenceExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ public static string TypeSignatureAsString(this TypeReference type)
9797
return byrefSig.ToString();
9898
}
9999

100-
if (type.IsGenericParameter)
100+
if (type.IsGenericParameter ||
101+
type.IsGenericInstance)
101102
{
102103
return $"!!{type.Name}";
103104
}

MetadataProcessor.Core/MetadataProcessor.Core.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<Compile Include="DumpGenerator\ExceptionHandler.cs" />
5656
<Compile Include="DumpGenerator\AttributeCustom.cs" />
5757
<Compile Include="DumpGenerator\AttFixedArgs.cs" />
58+
<Compile Include="DumpGenerator\GenericParam.cs" />
5859
<Compile Include="DumpGenerator\UserString.cs" />
5960
<Compile Include="DumpGenerator\ILCode.cs" />
6061
<Compile Include="DumpGenerator\LocalDef.cs" />
@@ -92,6 +93,7 @@
9293
<Compile Include="Tables\nanoFieldDefinitionTable.cs" />
9394
<Compile Include="Tables\nanoFieldReferenceTable.cs" />
9495
<Compile Include="Tables\nanoMethodDefinitionTable.cs" />
96+
<Compile Include="Tables\nanoGenericParamTable.cs" />
9597
<Compile Include="Tables\nanoMethodReferenceTable.cs" />
9698
<Compile Include="Tables\nanoReferenceTableBase.cs" />
9799
<Compile Include="Tables\nanoResourceDataTable.cs" />
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//
2+
// Copyright (c) .NET Foundation and Contributors
3+
// See LICENSE file in the project root for full license information.
4+
//
5+
6+
using Mono.Cecil;
7+
using System;
8+
using System.Collections.Generic;
9+
10+
namespace nanoFramework.Tools.MetadataProcessor
11+
{
12+
/// <summary>
13+
/// Encapsulates logic for storing generic parameters list and writing
14+
/// this collected list into target assembly in .NET nanoFramework format.
15+
/// </summary>
16+
public sealed class nanoGenericParamTable :
17+
nanoReferenceTableBase<GenericParameter>
18+
{
19+
/// <summary>
20+
/// Helper class for comparing two instances of <see cref="GenericParameter"/> objects
21+
/// using <see cref="MetadataToken"/> property as unique key for comparison.
22+
/// </summary>
23+
private sealed class MemberReferenceComparer : IEqualityComparer<GenericParameter>
24+
{
25+
/// <inheritdoc/>
26+
public bool Equals(GenericParameter x, GenericParameter y)
27+
{
28+
if (x is null)
29+
{
30+
throw new ArgumentNullException(nameof(x));
31+
}
32+
33+
if (y is null)
34+
{
35+
throw new ArgumentNullException(nameof(y));
36+
}
37+
38+
return x.MetadataToken.ToInt32() == y.MetadataToken.ToInt32();
39+
}
40+
41+
/// <inheritdoc/>
42+
public int GetHashCode(GenericParameter obj)
43+
{
44+
return obj.MetadataToken.ToInt32().GetHashCode();
45+
}
46+
}
47+
48+
/// <summary>
49+
/// Creates new instance of <see cref="nanoGenericParamTable"/> object.
50+
/// </summary>
51+
/// <param name="items">List of member references in Mono.Cecil format.</param>
52+
/// <param name="context">
53+
/// Assembly tables context - contains all tables used for building target assembly.
54+
/// </param>
55+
public nanoGenericParamTable(
56+
IEnumerable<GenericParameter> items,
57+
nanoTablesContext context)
58+
: base(items, new MemberReferenceComparer(), context)
59+
{
60+
}
61+
62+
/// <summary>
63+
/// Gets method reference ID if possible (if method is external and stored in this table).
64+
/// </summary>
65+
/// <param name="genericParameter">Method reference metadata in Mono.Cecil format.</param>
66+
/// <param name="referenceId">Method reference ID in .NET nanoFramework format.</param>
67+
/// <returns>Returns <c>true</c> if reference found, otherwise returns <c>false</c>.</returns>
68+
public bool TryGetParameterId(
69+
GenericParameter genericParameter,
70+
out ushort referenceId)
71+
{
72+
return TryGetIdByValue(genericParameter, out referenceId);
73+
}
74+
75+
/// <inheritdoc/>
76+
protected override void WriteSingleItem(
77+
nanoBinaryWriter writer,
78+
GenericParameter item)
79+
{
80+
if (!_context.MinimizeComplete)
81+
{
82+
return;
83+
}
84+
85+
// TODO
86+
}
87+
}
88+
}

MetadataProcessor.Core/Tables/nanoSignaturesTable.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,10 +665,19 @@ private void WriteSubTypeInfo(TypeReference typeDefinition, nanoBinaryWriter wri
665665
writer.WriteMetadataToken(((uint)referenceId << 2) | 0x01);
666666
}
667667
else if (_context.TypeDefinitionTable.TryGetTypeReferenceId(
668-
typeDefinition.Resolve(), out referenceId))
668+
typeDefinition.Resolve(),
669+
out referenceId))
669670
{
670671
writer.WriteMetadataToken((uint)referenceId << 2);
671672
}
673+
else if (typeDefinition.Resolve().HasGenericParameters &&
674+
_context.GenericParamsTable.TryGetParameterId(
675+
typeDefinition.Resolve().GenericParameters.FirstOrDefault(),
676+
out referenceId))
677+
{
678+
// TODO
679+
writer.WriteMetadataToken((uint)referenceId << 2);
680+
}
672681
else
673682
{
674683
throw new ArgumentException($"Can't find entry in type reference table for {typeDefinition.FullName}.");

MetadataProcessor.Core/Tables/nanoTablesContext.cs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,17 @@ public nanoTablesContext(
115115
var memberReferences = mainModule.GetMemberReferences()
116116
.Where(item =>
117117
(typeReferencesNames.Contains(item.DeclaringType.FullName) ||
118-
item.DeclaringType.GetElementType().IsPrimitive))
118+
item.DeclaringType.GetElementType().IsPrimitive ||
119+
item.ContainsGenericParameter ||
120+
item.DeclaringType.IsGenericInstance))
121+
119122
.ToList();
120123

121124
FieldReferencesTable = new nanoFieldReferenceTable(
122125
memberReferences.OfType<FieldReference>(), this);
123126
MethodReferencesTable = new nanoMethodReferenceTable(
124127
memberReferences.OfType<MethodReference>(), this);
125-
128+
126129
// Internal types definitions
127130

128131
var types = GetOrderedTypes(mainModule, explicitTypesOrder);
@@ -133,7 +136,6 @@ public nanoTablesContext(
133136
.SelectMany(item => GetOrderedFields(item.Fields.Where(field => !field.HasConstant)))
134137
.ToList();
135138
FieldsTable = new nanoFieldDefinitionTable(fields, this);
136-
137139
var methods = types.SelectMany(item => GetOrderedMethods(item.Methods)).ToList();
138140

139141
MethodDefinitionTable = new nanoMethodDefinitionTable(methods, this);
@@ -172,6 +174,24 @@ public nanoTablesContext(
172174

173175
ResourceFileTable = new nanoResourceFileTable(this);
174176

177+
// build list of generic parameters belonging to method defs
178+
List<GenericParameter> methodDefsGenericParameters = new List<GenericParameter>();
179+
180+
foreach (var m in methods)
181+
{
182+
if (m.HasGenericParameters)
183+
{
184+
methodDefsGenericParameters.AddRange(m.GenericParameters);
185+
}
186+
}
187+
188+
var generics = types
189+
.SelectMany(t => t.GenericParameters)
190+
.Concat(methodDefsGenericParameters)
191+
.ToList();
192+
193+
GenericParamsTable = new nanoGenericParamTable(generics, this);
194+
175195
// Pre-allocate strings from some tables
176196
AssemblyReferenceTable.AllocateStrings();
177197
TypeReferencesTable.AllocateStrings();
@@ -223,6 +243,8 @@ public ushort GetMethodReferenceId(
223243

224244
public nanoFieldReferenceTable FieldReferencesTable { get; private set; }
225245

246+
public nanoGenericParamTable GenericParamsTable { get; private set; }
247+
226248
public nanoMethodReferenceTable MethodReferencesTable { get; private set; }
227249

228250
public nanoFieldDefinitionTable FieldsTable { get; private set; }

MetadataProcessor.Core/nanoAssemblyBuilder.cs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//
66

77
using Mono.Cecil;
8+
using Mono.Cecil.Cil;
89
using Mono.Collections.Generic;
910
using nanoFramework.Tools.MetadataProcessor.Core.Extensions;
1011
using System;
@@ -514,6 +515,15 @@ private HashSet<MetadataToken> BuildDependencyList(MetadataToken token)
514515
}
515516
}
516517

518+
// generic parameters
519+
foreach (var g in td.GenericParameters)
520+
{
521+
if (!nanoTablesContext.ClassNamesToExclude.Contains(g.DeclaringType.FullName))
522+
{
523+
set.Add(g.MetadataToken);
524+
}
525+
}
526+
517527
// methods
518528
foreach (var m in td.Methods)
519529
{
@@ -620,6 +630,15 @@ private HashSet<MetadataToken> BuildDependencyList(MetadataToken token)
620630
set.Add(md.ReturnType.MetadataToken);
621631
}
622632

633+
// generic parameters
634+
if(md.HasGenericParameters)
635+
{
636+
foreach (var gp in md.GenericParameters)
637+
{
638+
set.Add(gp.MetadataToken);
639+
}
640+
}
641+
623642
// parameters
624643
foreach (var p in md.Parameters)
625644
{
@@ -707,7 +726,9 @@ private HashSet<MetadataToken> BuildDependencyList(MetadataToken token)
707726
i.Operand is FieldReference ||
708727
i.Operand is TypeDefinition ||
709728
i.Operand is TypeSpecification ||
710-
i.Operand is TypeReference)
729+
i.Operand is TypeReference ||
730+
i.Operand is GenericInstanceType ||
731+
i.Operand is GenericParameter)
711732
{
712733
set.Add(((IMetadataTokenProvider)i.Operand).MetadataToken);
713734
}
@@ -763,6 +784,16 @@ i.Operand is TypeSpecification ||
763784
}
764785

765786
break;
787+
788+
case TokenType.GenericParam:
789+
case TokenType.AssemblyRef:
790+
case TokenType.String:
791+
// we are good with these, nothing to do here
792+
break;
793+
794+
default:
795+
Debug.Fail($"Unable to process token {token}.");
796+
break;
766797
}
767798

768799
return set;
@@ -823,6 +854,26 @@ private string TokenToString(MetadataToken token)
823854
output.Append(fd.Name);
824855
break;
825856

857+
case TokenType.GenericParam:
858+
var gp = _tablesContext.GenericParamsTable.Items.FirstOrDefault(g => g.MetadataToken == token);
859+
860+
output.Append($"[GenericParam 0x{token.ToUInt32().ToString("X8")}]");
861+
862+
if (gp.DeclaringType != null)
863+
{
864+
output.Append(TokenToString(gp.DeclaringType.MetadataToken));
865+
output.Append("::");
866+
}
867+
else if(gp.DeclaringMethod != null)
868+
{
869+
output.Append(TokenToString(gp.DeclaringMethod.MetadataToken));
870+
output.Append("::");
871+
}
872+
873+
output.Append(gp.Name);
874+
875+
break;
876+
826877
case TokenType.Method:
827878
var md = _tablesContext.MethodDefinitionTable.Items.FirstOrDefault(i => i.MetadataToken == token);
828879

@@ -879,6 +930,17 @@ private string TokenToString(MetadataToken token)
879930
typeRef = fr.DeclaringType;
880931
typeName = fr.Name;
881932
}
933+
else
934+
{
935+
// try now with generic parameters
936+
var gr = _tablesContext.GenericParamsTable.Items.FirstOrDefault(g => g.MetadataToken == token);
937+
938+
if (gr != null)
939+
{
940+
typeRef = gr.DeclaringType;
941+
typeName = gr.Name;
942+
}
943+
}
882944
}
883945

884946
Debug.Assert(typeRef != null);
@@ -916,6 +978,10 @@ private string TokenToString(MetadataToken token)
916978
output.Append($"'{sr}'");
917979
}
918980
break;
981+
982+
default:
983+
Debug.Fail($"Unable to process token {token}.");
984+
break;
919985
}
920986

921987
// output token ID if empty

MetadataProcessor.Core/nanoDumperGenerator.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,21 @@ private void DumpTypeDefinitions(DumpAllTable dumpTable)
224224
typeDef.EnclosedType = token.ToInt32().ToString("x8");
225225
}
226226

227+
// list generic parameters
228+
foreach (var gp in t.GenericParameters)
229+
{
230+
var genericParam = new GenericParam()
231+
{
232+
Position = gp.Position.ToString(),
233+
GenericParamToken = gp.MetadataToken.ToInt32().ToString("x8"),
234+
Name = gp.FullName,
235+
Owner = gp.Owner.MetadataToken.ToInt32().ToString("x8"),
236+
Signature = gp.DeclaringType.Name
237+
};
238+
239+
typeDef.GenericParameters.Add(genericParam);
240+
}
241+
227242
// list type fields
228243
foreach (var f in t.Fields)
229244
{

0 commit comments

Comments
 (0)