forked from MochiLibraries/Biohazrd
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathStripUnreferencedLazyDeclarationsTransformation.cs
130 lines (105 loc) · 5.9 KB
/
StripUnreferencedLazyDeclarationsTransformation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
using Biohazrd.Metadata;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
namespace Biohazrd.Transformation.Common
{
/// <summary>Strips any declarations marked with <see cref="LazilyGenerated"/> which are not referenced.</summary>
public sealed class StripUnreferencedLazyDeclarationsTransformation : TransformationBase
{
private readonly HashSet<TranslatedDeclaration> ReferencedLazyDeclarations = new(ReferenceEqualityComparer.Instance);
private readonly ReferenceWalker Walker = new();
private sealed class ReferenceWalker : __TypeReferenceVisitor
{
// We're really using this more like HashSet<(TranslatedDeclaration, VisitorContext)>, but we want to use reference equality on the declaration and tuples don't make that easy.
private Dictionary<TranslatedDeclaration, VisitorContext> FoundLazyDeclarations = new(ReferenceEqualityComparer.Instance);
public int Count => FoundLazyDeclarations.Count;
public void Clear()
=> FoundLazyDeclarations.Clear();
public Dictionary<TranslatedDeclaration, VisitorContext> GetFoundLazyDeclarationsAndClear()
{
Dictionary<TranslatedDeclaration, VisitorContext> result = FoundLazyDeclarations;
FoundLazyDeclarations = new Dictionary<TranslatedDeclaration, VisitorContext>(ReferenceEqualityComparer.Instance);
return result;
}
protected override void VisitRecord(VisitorContext context, TranslatedRecord declaration)
=> VisitRecord(context, declaration, isDirectVisit: false);
public void VisitDirect(VisitorContext context, TranslatedDeclaration declaration)
{
if (declaration is TranslatedRecord record)
{
Debug.Assert(declaration.Metadata.Has<LazilyGenerated>());
VisitRecord(context, record, isDirectVisit: true);
}
else
{ base.Visit(context, declaration); }
}
private void VisitRecord(VisitorContext context, TranslatedRecord declaration, bool isDirectVisit)
{
// If this isn't a direct visit and the declaration is lazy, skip it to avoid counting a lazy declaration as referenced when it is referenced by
// its self or another lazy declaration (If this is a direct visit, we know this lazy declaration has already been referenced.)
if (!isDirectVisit && declaration.Metadata.Has<LazilyGenerated>())
{ return; }
base.VisitRecord(context, declaration);
}
protected override void VisitTypeReference(VisitorContext context, ImmutableArray<TypeReference> parentTypeReferences, TypeReference typeReference)
{
if (typeReference is not TranslatedTypeReference translatedTypeReference)
{ goto baseCall; }
VisitorContext resolvedContext;
TranslatedDeclaration? resolvedDeclaration = translatedTypeReference.TryResolve(context.Library, out resolvedContext);
if (resolvedDeclaration is null)
{ goto baseCall; }
// Check if the resolved declaration or any of its parental lineage are lazily generated and mark them as found
while (true)
{
if (resolvedDeclaration.Metadata.Has<LazilyGenerated>())
{ FoundLazyDeclarations.TryAdd(resolvedDeclaration, resolvedContext); }
resolvedDeclaration = resolvedContext.ParentDeclaration;
if (resolvedDeclaration is null)
{ break; }
resolvedContext = resolvedContext.MakePrevious();
}
baseCall:
base.VisitTypeReference(context, parentTypeReferences, typeReference);
}
}
protected override TranslatedLibrary PreTransformLibrary(TranslatedLibrary library)
{
Debug.Assert(ReferencedLazyDeclarations.Count == 0);
ReferencedLazyDeclarations.Clear();
Debug.Assert(Walker.Count == 0);
Walker.Clear();
// Enumerate all lazy declarations referenced by non-lazy declarations in the library
Walker.Visit(library);
// Loop so we keep adding new lazy declarations found to be referenced by lazy declarations processed by the previous iteration
while (Walker.Count > 0)
{
// Reset the walker and process its results
foreach ((TranslatedDeclaration lazyDeclaration, VisitorContext lazyContext) in Walker.GetFoundLazyDeclarationsAndClear())
{
// Try to record the referenced lazy declaration. If it was already seen before, there's nothing more to do
if (!ReferencedLazyDeclarations.Add(lazyDeclaration))
{ continue; }
// If this is the first time we've seen this lazy declaration, walk it to find any lazy declarations which it references
Walker.VisitDirect(lazyContext, lazyDeclaration);
}
}
return library;
}
protected override TransformationResult TransformRecord(TransformationContext context, TranslatedRecord declaration)
{
if (!declaration.Metadata.Has<LazilyGenerated>())
{ return declaration; }
if (ReferencedLazyDeclarations.Contains(declaration))
{ return declaration; }
// This lazily-generated declaration was not referenced, remove it
return null;
}
protected override TranslatedLibrary PostTransformLibrary(TranslatedLibrary library)
{
ReferencedLazyDeclarations.Clear();
return library;
}
}
}