-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExposeAssembly.cs
More file actions
129 lines (108 loc) · 4.34 KB
/
ExposeAssembly.cs
File metadata and controls
129 lines (108 loc) · 4.34 KB
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
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using dnlib.DotNet;
using Microsoft.Build.Framework;
namespace AssemblyExposer.MSBuild.Task;
public class ExposeAssembly : Microsoft.Build.Utilities.Task {
public virtual ITaskItem[]? SourceAssemblies { get; set; }
public virtual string OutputDirectory { get; set; } = @"lib\exposed";
public virtual ITaskItem[]? Rules { get; set; }
public override bool Execute() {
if (SourceAssemblies == null) {
Log.LogError($"Property {nameof(SourceAssemblies)} isn't set");
return false;
}
if (Rules == null) {
Log.LogError($"Property {nameof(Rules)} isn't set");
return false;
}
try {
WriteAssemblies();
} catch (Exception e) {
Log.LogErrorFromException(e);
return false;
}
return true;
}
private void WriteAssemblies() {
var rules = Rules.Select(it => new RewriteRule(it)).ToList();
foreach (var assemblyItem in SourceAssemblies!) {
var sourceAssembly = assemblyItem.ItemSpec;
if (!ProcessingRequired(sourceAssembly, rules))
continue;
var name = Path.GetFileName(sourceAssembly);
Directory.CreateDirectory(OutputDirectory);
using var assembly = ModuleDefMD.Load(sourceAssembly);
Log.LogMessage($"Rewriting visibility for {sourceAssembly}");
Apply(assembly, rules);
assembly.Write(Path.Combine(OutputDirectory, name));
SaveState(sourceAssembly, rules);
}
}
private string CalculateRulesHash(List<RewriteRule> rules) {
using var algorithm = SHA1.Create();
var data = rules.Aggregate(
new StringBuilder(),
(s, r) => s
.Append("###")
.Append(r.Pattern)
.Append("###")
.Append(r.Target.ToString())
.Append("###")
.Append(r.Visibility)
.Append("###")
).ToString();
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(data));
return Convert.ToBase64String(algorithm.ComputeHash(stream));
}
private string CalculateFileHash(string fileName) {
using var algorithm = SHA1.Create();
using var stream = File.OpenRead(fileName);
return Convert.ToBase64String(algorithm.ComputeHash(stream));
}
private string GetStateFileName(string assemblyPath) => Path.Combine(
OutputDirectory,
Path.GetFileName(assemblyPath) + ".state"
);
private bool ProcessingRequired(string assemblyFileName, List<RewriteRule> rules) {
var stateFileName = GetStateFileName(Path.GetFileName(assemblyFileName));
if (!File.Exists(stateFileName))
return true;
var hashes = File.ReadAllLines(stateFileName);
return CalculateRulesHash(rules) != hashes[0] || CalculateFileHash(assemblyFileName) != hashes[1];
}
private void SaveState(string assemblyFileName, List<RewriteRule> rules) {
File.WriteAllLines(
GetStateFileName(assemblyFileName),
new[] { CalculateRulesHash(rules), CalculateFileHash(assemblyFileName) }
);
}
private void Apply(ModuleDefMD assembly, List<RewriteRule> rules) {
foreach (var type in assembly.GetTypes()) {
ApplyRules(rules, type);
foreach (var method in type.Methods)
ApplyRules(rules, method);
foreach (var field in type.Fields) {
if (type.HasEvents) {
var eventDef = type.FindEvent(field.Name);
if (eventDef != null) {
Log.LogMessage($"Ignoring field '{field.FullName}' as it's related to the event '{eventDef.FullName}'");
continue;
}
}
ApplyRules(rules, field);
}
}
}
private void ApplyRules(List<RewriteRule> rules, IMemberDef def) {
var rule = rules.FirstOrDefault(rule => rule.Matches(def));
if (rule == null)
return;
def.ApplyRule(rule);
Log.LogMessage($"Rule '{rule.Name}' applied to '{def.ExtendedName()}'");
}
}