-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathRunner.cs
135 lines (117 loc) · 4.62 KB
/
Runner.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
131
132
133
134
135
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Basic.Reference.Assemblies;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace DecompilationDiffer;
internal class Runner
{
private readonly DecompilerSettings _decompilerSettings = new DecompilerSettings(ICSharpCode.Decompiler.CSharp.LanguageVersion.CSharp1)
{
ArrayInitializers = false,
AutomaticEvents = false,
DecimalConstants = false,
FixedBuffers = false,
UsingStatement = false,
SwitchStatementOnString = false,
LockStatement = false,
ForStatement = false,
ForEachStatement = false,
SparseIntegerSwitch = false,
DoWhileStatement = false,
StringConcat = false,
UseRefLocalsForAccurateOrderOfEvaluation = true,
InitAccessors = true,
FunctionPointers = true,
NativeIntegers = true
};
private static AssemblyResolver? s_assemblyResolver;
private readonly string _baseCode;
private readonly string _version1;
private readonly string _version2;
public string BaseOutput { get; private set; } = "";
public string Version1Output { get; private set; } = "";
public string Version2Output { get; private set; } = "";
public Runner(string baseCode, string version1, string version2)
{
_baseCode = baseCode;
_version1 = version1;
_version2 = version2;
}
internal void Run()
{
try
{
if (s_assemblyResolver == null)
{
s_assemblyResolver = new AssemblyResolver();
}
this.BaseOutput = "";
this.Version1Output = "";
this.Version2Output = "";
this.BaseOutput = CompileAndDecompile(_baseCode, "base");
this.Version1Output = CompileAndDecompile(_version1, "version 1");
this.Version2Output = CompileAndDecompile(_version2, "version 2");
}
catch (Exception ex)
{
this.BaseOutput = "Error doing something:\n\n" + ex.ToString();
}
}
private string CompileAndDecompile(string code, string name)
{
SyntaxTree? codeTree = CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(kind: SourceCodeKind.Regular).WithLanguageVersion(LanguageVersion.Preview), "Program.cs");
var outputKind = codeTree.GetCompilationUnitRoot().Members.Any(m => m is GlobalStatementSyntax)
? OutputKind.ConsoleApplication
: OutputKind.DynamicallyLinkedLibrary;
var codeCompilation = CSharpCompilation.Create("Program", new SyntaxTree[] { codeTree }, Net80.References.All, new CSharpCompilationOptions(outputKind, concurrentBuild: false));
var errors = GetErrors("Error compiling " + name + " code:\n\n", codeCompilation.GetDiagnostics());
if (errors != null)
{
return errors;
}
var assemblyStream = GetAssemblyStream(codeCompilation, out var rawErrors);
if (rawErrors is { Length: > 0 } || assemblyStream == null)
{
return "Error getting assembly stream for " + name + " code: " + rawErrors;
}
using var peFile = new PEFile("", assemblyStream);
var decompiler = new ICSharpCode.Decompiler.CSharp.CSharpDecompiler(peFile, s_assemblyResolver, _decompilerSettings);
return decompiler.DecompileWholeModuleAsString();
}
private static Stream? GetAssemblyStream(Compilation generatorCompilation, out string? errors)
{
try
{
var generatorStream = new MemoryStream();
Microsoft.CodeAnalysis.Emit.EmitResult? result = generatorCompilation.Emit(generatorStream);
if (!result.Success)
{
errors = GetErrors($"Error emitting aseembly:", result.Diagnostics, false);
return null;
}
generatorStream.Seek(0, SeekOrigin.Begin);
errors = null;
return generatorStream;
}
catch (Exception ex)
{
errors = ex.ToString();
return null;
}
}
private static string? GetErrors(string header, IEnumerable<Diagnostic> diagnostics, bool errorsOnly = true)
{
IEnumerable<Diagnostic>? errors = diagnostics.Where(d => !errorsOnly || d.Severity == DiagnosticSeverity.Error);
if (!errors.Any())
{
return null;
}
return header + Environment.NewLine + Environment.NewLine + string.Join(Environment.NewLine, errors);
}
}