Skip to content

Commit e624eb1

Browse files
authored
Fix handling of TypeSpec fields (#184)
1 parent 256b0d2 commit e624eb1

File tree

8 files changed

+137
-9
lines changed

8 files changed

+137
-9
lines changed

MetadataProcessor.Shared/Tables/nanoTablesContext.cs

+16-2
Original file line numberDiff line numberDiff line change
@@ -316,12 +316,12 @@ public ushort GetFieldReferenceId(
316316
// 0: TBL_FieldDef
317317
// 1: TBL_FieldRef
318318

319-
ushort referenceId = 0xFFFF;
319+
ushort referenceId;
320320
NanoClrTable ownerTable = NanoClrTable.TBL_EndOfAssembly;
321321

322322
if (fieldReference is FieldDefinition)
323323
{
324-
// field reference is internal
324+
// field definition is internal
325325
if (FieldsTable.TryGetFieldReferenceId(fieldReference as FieldDefinition, false, out referenceId))
326326
{
327327
// field reference is internal => field definition
@@ -337,6 +337,20 @@ public ushort GetFieldReferenceId(
337337
// field reference is external
338338
ownerTable = NanoClrTable.TBL_FieldRef;
339339
}
340+
else if (fieldReference is MemberReference)
341+
{
342+
// field reference belongs to a TypeSpec
343+
// find FieldDef for this
344+
FieldReference fieldRef = FieldReferencesTable.Items.FirstOrDefault(m => m.DeclaringType.MetadataToken == fieldReference.DeclaringType.MetadataToken && m.Name == fieldReference.Name);
345+
346+
if (!FieldReferencesTable.TryGetFieldReferenceId(fieldRef, out referenceId))
347+
{
348+
Debug.Fail($"Can't find FieldRef for {fieldReference}");
349+
}
350+
351+
// owner is FieldRef
352+
ownerTable = NanoClrTable.TBL_FieldRef;
353+
}
340354
else
341355
{
342356
Debug.Fail($"Can't find any reference for {fieldReference}");

MetadataProcessor.Shared/Tables/nanoTypeSpecificationsTable.cs

+15
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,21 @@ public TypeReference TryGetTypeSpecification(MetadataToken token)
130130
return _idByTypeSpecifications.FirstOrDefault(typeEntry => typeEntry.Key.MetadataToken == token).Key;
131131
}
132132

133+
/// <summary>
134+
/// Tries to find type reference by the index on the TypeSpec list.
135+
/// </summary>
136+
/// <param name="index">Index of the type reference in the list.</param>
137+
/// <returns>Returns the type reference if found, otherwise returns <c>null</c>.</returns>
138+
public TypeReference TryGetTypeReferenceByIndex(ushort index)
139+
{
140+
if (index >= _idByTypeSpecifications.Count)
141+
{
142+
return null;
143+
}
144+
145+
return _idByTypeSpecifications.ElementAt(index).Key;
146+
}
147+
133148
/// <inheritdoc/>
134149
public void Write(
135150
nanoBinaryWriter writer)

MetadataProcessor.Shared/nanoAssemblyBuilder.cs

+41-6
Original file line numberDiff line numberDiff line change
@@ -350,18 +350,20 @@ private HashSet<MetadataToken> BuildDependencyList(MetadataToken token)
350350
if (mr != null &&
351351
mr.ReturnType != null)
352352
{
353-
parameters = mr.Parameters;
354-
355353
if (mr.DeclaringType != null)
356354
{
357355
if (mr.DeclaringType is TypeSpecification)
358356
{
359357
// Cecil.Mono has a bug providing TypeSpecs Metadata tokens generic parameters variables, so we need to check against our internal table and build one from it
360358
if (_tablesContext.TypeSpecificationsTable.TryGetTypeReferenceId(mr.DeclaringType, out ushort referenceId))
361359
{
360+
// add "fabricated" token for TypeSpec using the referenceId as RID
362361
set.Add(new MetadataToken(
363362
TokenType.TypeSpec,
364363
referenceId));
364+
365+
// add the metadata token for the element type
366+
set.Add(mr.DeclaringType.GetElementType().MetadataToken);
365367
}
366368
else
367369
{
@@ -460,9 +462,13 @@ private HashSet<MetadataToken> BuildDependencyList(MetadataToken token)
460462
// Cecil.Mono has a bug providing TypeSpecs Metadata tokens generic parameters variables, so we need to check against our internal table and build one from it
461463
if (_tablesContext.TypeSpecificationsTable.TryGetTypeReferenceId(fr.DeclaringType, out ushort referenceId))
462464
{
465+
// add "fabricated" token for TypeSpec using the referenceId as RID
463466
set.Add(new MetadataToken(
464467
TokenType.TypeSpec,
465468
referenceId));
469+
470+
// add the metadata token for the element type
471+
set.Add(fr.DeclaringType.GetElementType().MetadataToken);
466472
}
467473
else
468474
{
@@ -518,14 +524,33 @@ private HashSet<MetadataToken> BuildDependencyList(MetadataToken token)
518524
break;
519525

520526
case TokenType.TypeSpec:
521-
var ts = _tablesContext.TypeSpecificationsTable.TryGetTypeSpecification(token);
522-
523-
if (ts != null)
527+
// Developer notes:
528+
// Because the issue with Mono.Cecil not providing the correct metadata token for TypeSpec,
529+
// need to search the TypeSpec token in the two "formats".
530+
// 1. The original token as provided by Mono.Cecil
531+
// 2. The "fabricated" token, which is the one that is used in the TypeSpec table
532+
// It's OK to add both to the set because they end up referencing to the same type.
533+
// Anyways, as this a minimize operation, it's preferable to have both rather than none.
534+
535+
// start searching for metadata token
536+
TypeReference ts1 = _tablesContext.TypeSpecificationsTable.TryGetTypeSpecification(token);
537+
538+
if (ts1 != null)
524539
{
540+
// found it, let's add it
525541
set.Add(token);
526542
}
527543

528-
Debug.Assert(ts != null);
544+
// now try to find the TypeSpec from the "fabricated" token, using the RID
545+
TypeReference ts2 = _tablesContext.TypeSpecificationsTable.TryGetTypeReferenceByIndex((ushort)token.RID);
546+
547+
if (ts2 != null)
548+
{
549+
set.Add(ts2.MetadataToken);
550+
}
551+
552+
// sanity check
553+
Debug.Assert(ts1 != null || ts2 != null);
529554

530555
break;
531556

@@ -807,10 +832,12 @@ private HashSet<MetadataToken> BuildDependencyList(MetadataToken token)
807832
// Cecil.Mono has a bug providing TypeSpecs Metadata tokens generic parameters variables, so we need to check against our internal table and build one from it
808833
if (_tablesContext.TypeSpecificationsTable.TryGetTypeReferenceId(v.VariableType, out ushort referenceId))
809834
{
835+
// add "fabricated" token for TypeSpec using the referenceId as RID
810836
set.Add(new MetadataToken(
811837
TokenType.TypeSpec,
812838
referenceId));
813839

840+
// add the metadata token for the element type
814841
set.Add(v.VariableType.GetElementType().MetadataToken);
815842
}
816843
else
@@ -844,9 +871,13 @@ private HashSet<MetadataToken> BuildDependencyList(MetadataToken token)
844871
// Cecil.Mono has a bug providing TypeSpecs Metadata tokens generic parameters variables, so we need to check against our internal table and build one from it
845872
if (_tablesContext.TypeSpecificationsTable.TryGetTypeReferenceId(methodReferenceType.DeclaringType, out referenceId))
846873
{
874+
// add "fabricated" token for TypeSpec using the referenceId as RID
847875
set.Add(new MetadataToken(
848876
TokenType.TypeSpec,
849877
referenceId));
878+
879+
// add the metadata token for the element type
880+
set.Add(methodReferenceType.DeclaringType.GetElementType().MetadataToken);
850881
}
851882
else
852883
{
@@ -877,9 +908,13 @@ i.Operand is GenericInstanceMethod ||
877908
// Cecil.Mono has a bug providing TypeSpecs Metadata tokens generic parameters variables, so we need to check against our internal table and build one from it
878909
if (_tablesContext.TypeSpecificationsTable.TryGetTypeReferenceId(opType, out ushort referenceId))
879910
{
911+
// add "fabricated" token for TypeSpec using the referenceId as RID
880912
set.Add(new MetadataToken(
881913
TokenType.TypeSpec,
882914
referenceId));
915+
916+
// add the metadata token for the element type
917+
set.Add(opType.GetElementType().MetadataToken);
883918
}
884919
else
885920
{

MetadataProcessor.Shared/nanoDumperGenerator.cs

+19
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,25 @@ private void DumpTypeSpecifications(
734734
}
735735
}
736736

737+
foreach (var fr in _tablesContext.FieldReferencesTable.Items)
738+
{
739+
if (_tablesContext.TypeSpecificationsTable.TryGetTypeReferenceId(fr.DeclaringType, out ushort referenceId) &&
740+
referenceId == index)
741+
{
742+
var memberRef = new MemberRef()
743+
{
744+
Name = fr.Name
745+
};
746+
if (_tablesContext.FieldReferencesTable.TryGetFieldReferenceId(fr, out ushort fieldRefId))
747+
{
748+
realToken = fr.MetadataToken.ToInt32().ToString("X8");
749+
memberRef.ReferenceId = $"[{new nanoMetadataToken(NanoClrTable.TBL_FieldRef, fieldRefId)}] /*{realToken}*/";
750+
memberRef.Signature = fr.FieldType.TypeSignatureAsString();
751+
}
752+
typeSpec.MemberReferences.Add(memberRef);
753+
}
754+
}
755+
737756
Debug.Assert(typeSpec.MemberReferences.Count > 0, $"Couldn't find any MethodRef for TypeSpec[{typeReference}] {typeReference.FullName}");
738757
}
739758

MetadataProcessor.Tests/Core/Utility/DumperTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public void DumpAssemblyTest()
133133
nanoTablesContext.TypeReferencesTable.TryGetTypeReferenceId(type1.BaseType, out baseTypeReferenceId);
134134
typeFlags = (uint)nanoTypeDefinitionTable.GetFlags(type1, nanoTablesContext.MethodDefinitionTable);
135135

136-
Assert.IsTrue(dumpFileContent.Contains($"TypeDef {nanoDumperGenerator.TypeDefRefIdToString(type1, typeRefId)}\r\n-------------------------------------------------------\r\n '{type1.Name}'\r\n Flags: {typeFlags:X8}\r\n Extends: {nanoDumperGenerator.TypeDefExtendsTypeToString(type1.BaseType, baseTypeReferenceId)}\r\n Enclosed: TestNFApp.OneClassOverAll[04000000] /*0200001B*/"));
136+
Assert.IsTrue(dumpFileContent.Contains($"TypeDef {nanoDumperGenerator.TypeDefRefIdToString(type1, typeRefId)}\r\n-------------------------------------------------------\r\n '{type1.Name}'\r\n Flags: {typeFlags:X8}\r\n Extends: {nanoDumperGenerator.TypeDefExtendsTypeToString(type1.BaseType, baseTypeReferenceId)}\r\n Enclosed: TestNFApp.OneClassOverAll[04000000] /*0200001D*/"));
137137

138138
// String heap
139139
foreach (string stringKey in nanoTablesContext.StringTable.GetItems().Keys)

MetadataProcessor.Tests/TestNFApp/Program.cs

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public static void Main()
8080
///////////////////////////////////////////////////////////////////
8181
// Generics Tests
8282
_ = new GenericClassTests();
83+
_ = new StatckTests();
8384

8485
// null attributes tests
8586
Console.WriteLine("Null attributes tests");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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;
5+
6+
namespace TestNFApp
7+
{
8+
public class Stack<T>
9+
{
10+
private readonly T[] _items;
11+
private int _count;
12+
13+
public Stack(int size) => _items = new T[size];
14+
15+
public void Push(T item) => _items[_count++] = item;
16+
17+
public T Pop() => _items[--_count];
18+
}
19+
20+
public class StatckTests
21+
{
22+
public StatckTests()
23+
{
24+
// Create a stack of integers
25+
Stack<int> intStack = new Stack<int>(5);
26+
27+
intStack.Push(1);
28+
intStack.Push(2);
29+
30+
Console.WriteLine($"First value is{intStack.Pop()}");
31+
Console.WriteLine($"Second value is{intStack.Pop()}");
32+
33+
// Create a stack of strings
34+
Stack<string> stringStack = new Stack<string>(5);
35+
36+
stringStack.Push("Hello");
37+
stringStack.Push("World");
38+
39+
Console.WriteLine($"First value is {stringStack.Pop()}");
40+
Console.WriteLine($"Second value is {stringStack.Pop()}");
41+
}
42+
}
43+
}

MetadataProcessor.Tests/TestNFApp/TestNFApp.nfproj

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<Compile Include="DataRowAttribute.cs" />
2424
<Compile Include="DummyCustomAttribute1.cs" />
2525
<Compile Include="DummyCustomAttribute2.cs" />
26+
<Compile Include="StackClass.cs" />
2627
<Compile Include="IgnoreAttribute.cs" />
2728
<Compile Include="IOneClassOverAll.cs" />
2829
<Compile Include="ComplexAttribute.cs" />

0 commit comments

Comments
 (0)