Skip to content

Commit

Permalink
(#38) ImportLogic: finalize the importing for internal types
Browse files Browse the repository at this point in the history
  • Loading branch information
ForNeVeR committed Dec 28, 2024
1 parent 28babec commit 1c5cfd4
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 52 deletions.
2 changes: 1 addition & 1 deletion src/Refasmer/Importer/Caches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public partial class MetadataImporter
private readonly Dictionary<InterfaceImplementationHandle, InterfaceImplementationHandle> _interfaceImplementationCache = new();
private readonly Dictionary<GenericParameterHandle, GenericParameterHandle> _genericParameterCache = new();
private readonly Dictionary<GenericParameterConstraintHandle, GenericParameterConstraintHandle> _genericParameterConstraintCache = new();
private readonly Dictionary<TypeDefinitionHandle, TypeDefinitionHandle> _typeDefinitionCache = new();
private Dictionary<TypeDefinitionHandle, TypeDefinitionHandle> _typeDefinitionCache = null!;
private readonly Dictionary<MethodDefinitionHandle, MethodDefinitionHandle> _methodDefinitionCache = new();
private readonly Dictionary<FieldDefinitionHandle, FieldDefinitionHandle> _fieldDefinitionCache = new();
private readonly Dictionary<PropertyDefinitionHandle, PropertyDefinitionHandle> _propertyDefinitionCache = new();
Expand Down
121 changes: 72 additions & 49 deletions src/Refasmer/Importer/ImportLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -431,64 +431,27 @@ public ReservedBlob<GuidHandle> Import()
foreach (var srcHandle in _reader.AssemblyFiles)
Import(srcHandle);

var index = 1;
Debug?.Invoke("Preparing type list for import");

var checker = new CachedAttributeChecker();
// 1. Process/assign numbers to the initial set of the imported types.
var internalTypesToPreserve = new HashSet<TypeDefinitionHandle>();
var initialTypeDefinitions = TypeImportPass(_reader, internalTypesToPreserve, Filter, Trace);

foreach (var srcHandle in _reader.TypeDefinitions)
if (Filter?.OmitNonApiMembers == true)
{
bool shouldImport;

var src = _reader.GetTypeDefinition(srcHandle);

// Special <Module> type
if (srcHandle.GetHashCode() == 1 && _reader.GetString(src.Name) == "<Module>")
{
shouldImport = true;
}
else if (checker.HasAttribute(_reader, src, FullNames.Embedded) &&
checker.HasAttribute(_reader, src, FullNames.CompilerGenerated))
{
Trace?.Invoke($"Embedded type found {_reader.ToString(srcHandle)}");
shouldImport = true;
}
else if (_reader.GetString(src.Namespace) == FullNames.CompilerServices &&
_reader.GetFullname(src.BaseType) == FullNames.Attribute)
{
Trace?.Invoke($"CompilerServices attribute found {_reader.ToString(srcHandle)}");
shouldImport = true;
}
else if (_reader.GetString(src.Namespace) == FullNames.CodeAnalysis &&
_reader.GetFullname(src.BaseType) == FullNames.Attribute)
{
Trace?.Invoke($"CodeAnalysis attribute found {_reader.ToString(srcHandle)}");
shouldImport = true;
}
else
// 2. If required, seek for the additional internal types to import.
Debug?.Invoke("Enumerating the internal types for import.");
foreach (var internalTypeHandle in CalculateInternalTypesToPreserve(initialTypeDefinitions.Keys))
{
shouldImport = Filter?.AllowImport(_reader.GetTypeDefinition(srcHandle), _reader) != false;
internalTypesToPreserve.Add(internalTypeHandle);
}

if (shouldImport)
{
_typeDefinitionCache[srcHandle] = MetadataTokens.TypeDefinitionHandle(index++);
}
else
{
Trace?.Invoke($"Type filtered and will not be imported {_reader.ToString(srcHandle)}");
}
// 3. Enumerate the imported types again, to assign proper final numbering.
_typeDefinitionCache = TypeImportPass(_reader, internalTypesToPreserve, Filter, Trace);
}

var internalTypesToPreserve = new HashSet<TypeDefinitionHandle>();
if (Filter?.OmitNonApiMembers == true)
else
{
Debug?.Invoke("Generating internal type stubs.");
foreach (var internalTypeHandle in CalculateInternalTypesToPreserve(_typeDefinitionCache.Keys))
{
internalTypesToPreserve.Add(internalTypeHandle);
_typeDefinitionCache[internalTypeHandle] = MetadataTokens.TypeDefinitionHandle(index++);
}
_typeDefinitionCache = initialTypeDefinitions;
}

Debug?.Invoke("Importing type definitions");
Expand Down Expand Up @@ -566,6 +529,66 @@ public ReservedBlob<GuidHandle> Import()
return mvidBlob;
}

private static Dictionary<TypeDefinitionHandle, TypeDefinitionHandle> TypeImportPass(
MetadataReader reader,
HashSet<TypeDefinitionHandle> internalTypesToPreserve,
IImportFilter? filter,
Action<string>? traceLog)
{
var checker = new CachedAttributeChecker();
var typeDefinitions = new Dictionary<TypeDefinitionHandle, TypeDefinitionHandle>();
var index = 1;
foreach (var srcHandle in reader.TypeDefinitions)
{
bool shouldImport;

var src = reader.GetTypeDefinition(srcHandle);

// Special <Module> type
if (srcHandle.GetHashCode() == 1 && reader.GetString(src.Name) == "<Module>")
{
shouldImport = true;
}
else if (checker.HasAttribute(reader, src, FullNames.Embedded) &&
checker.HasAttribute(reader, src, FullNames.CompilerGenerated))
{
traceLog?.Invoke($"Embedded type found {reader.ToString(srcHandle)}");
shouldImport = true;
}
else if (reader.GetString(src.Namespace) == FullNames.CompilerServices &&
reader.GetFullname(src.BaseType) == FullNames.Attribute)
{
traceLog?.Invoke($"CompilerServices attribute found {reader.ToString(srcHandle)}");
shouldImport = true;
}
else if (reader.GetString(src.Namespace) == FullNames.CodeAnalysis &&
reader.GetFullname(src.BaseType) == FullNames.Attribute)
{
traceLog?.Invoke($"CodeAnalysis attribute found {reader.ToString(srcHandle)}");
shouldImport = true;
}
else if (internalTypesToPreserve.Contains(srcHandle))
{
shouldImport = true;
}
else
{
shouldImport = filter?.AllowImport(reader.GetTypeDefinition(srcHandle), reader) != false;
}

if (shouldImport)
{
typeDefinitions[srcHandle] = MetadataTokens.TypeDefinitionHandle(index++);
}
else
{
traceLog?.Invoke($"Type filtered and will not be imported {reader.ToString(srcHandle)}");
}
}

return typeDefinitions;
}

/// <remarks>
/// The point of this method is to make a value type non-empty in case we've decided to skip all its fields.
/// </remarks>
Expand Down
2 changes: 1 addition & 1 deletion tests/Refasmer.Tests/IntegrationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ protected static Task VerifyTypeContents(string assemblyPath, string[] typeNames
{
var type = assembly.MainModule.GetType(typeName);
Assert.That(
assembly.MainModule.GetType(typeName),
type,
Is.Not.Null,
$"Type \"{typeName}\" is not found in assembly \"{assemblyPath}\".");

Expand Down
2 changes: 1 addition & 1 deletion tests/Refasmer.Tests/IntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ public async Task InternalTypeInPublicApi()
var resultAssembly = RefasmTestAssembly(assemblyPath, omitNonApiMembers: true);
await VerifyTypeContents(
resultAssembly,
["RefasmerTestAssembly.PublicClassWithInternalTypeInApi", "RefasmerTestAssembly.ClassToBeMarkedAsInternal"]);
["RefasmerTestAssembly.PublicClassWithInternalTypeInApi", "RefasmerTestAssembly.ClassToBeMarkedInternal"]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public type: RefasmerTestAssembly.PublicClassWithInternalTypeInApi
methods:
- Accept(RefasmerTestAssembly.ClassToBeMarkedInternal argument): System.Void:
- .ctor(): System.Void:
internal type: RefasmerTestAssembly.ClassToBeMarkedInternal

0 comments on commit 1c5cfd4

Please sign in to comment.