forked from MapsterMapper/Mapster
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExpressionCompiler.cs
More file actions
136 lines (111 loc) · 5.26 KB
/
ExpressionCompiler.cs
File metadata and controls
136 lines (111 loc) · 5.26 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
130
131
132
133
134
135
136
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace ExpressionDebugger
{
public class ExpressionCompiler
{
public List<ExpressionTranslator> Translators { get; } = new List<ExpressionTranslator>();
private readonly ExpressionCompilationOptions? _options;
public ExpressionCompiler(ExpressionCompilationOptions? options = null)
{
_options = options;
}
private readonly List<SyntaxTree> _codes = new List<SyntaxTree>();
public void AddFile(string code, string filename)
{
var buffer = Encoding.UTF8.GetBytes(code);
var path = filename;
if (_options?.EmitFile == true)
{
var root = _options?.RootPath
?? Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "GeneratedSources");
Directory.CreateDirectory(root);
path = Path.Combine(root, filename);
using var fs = new FileStream(path, FileMode.Create);
fs.Write(buffer, 0, buffer.Length);
}
var sourceText = SourceText.From(buffer, buffer.Length, Encoding.UTF8, canBeEmbedded: true);
var syntaxTree = CSharpSyntaxTree.ParseText(
sourceText,
new CSharpParseOptions(),
path);
var syntaxRootNode = syntaxTree.GetRoot() as CSharpSyntaxNode;
var encoded = CSharpSyntaxTree.Create(syntaxRootNode, null, path, Encoding.UTF8);
_codes.Add(encoded);
}
public void AddFile(LambdaExpression node, ExpressionDefinitions? definitions = null)
{
definitions ??= _options?.DefaultDefinitions ?? new ExpressionDefinitions {IsStatic = true};
definitions.TypeName ??= "Program";
var translator = ExpressionTranslator.Create(node, definitions);
var script = translator.ToString();
Translators.Add(translator);
this.AddFile(script, Path.ChangeExtension(Path.GetRandomFileName(), ".cs"));
}
public Assembly CreateAssembly()
{
var references = new HashSet<Assembly>();
references.UnionWith(from t in Translators
from n in t.TypeNames
select n.Key.Assembly);
if (_options?.References != null)
references.UnionWith(_options.References);
references.Add(typeof(object).Assembly);
#if NETSTANDARD2_0 || NET6_0_OR_GREATER
references.Add(Assembly.Load(new AssemblyName("netstandard")));
references.Add(Assembly.Load(new AssemblyName("System.Runtime")));
references.Add(Assembly.Load(new AssemblyName("System.Collections")));
#endif
var assemblyName = Path.GetRandomFileName();
var symbolsName = Path.ChangeExtension(assemblyName, "pdb");
var metadataReferences = references.Select(it => MetadataReference.CreateFromFile(it.Location));
var isRelease = _options?.IsRelease ?? !Debugger.IsAttached;
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
_codes,
metadataReferences,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, usings: new[] { "System" })
.WithOptimizationLevel(isRelease ? OptimizationLevel.Release : OptimizationLevel.Debug)
.WithPlatform(Platform.AnyCpu)
);
using var assemblyStream = new MemoryStream();
using var symbolsStream = new MemoryStream();
var emitOptions = new EmitOptions(
debugInformationFormat: DebugInformationFormat.PortablePdb,
pdbFilePath: symbolsName);
var embeddedTexts = _codes.Select(it => EmbeddedText.FromSource(it.FilePath, it.GetText()));
EmitResult result = compilation.Emit(
peStream: assemblyStream,
pdbStream: symbolsStream,
embeddedTexts: embeddedTexts,
options: emitOptions);
if (!result.Success)
{
var errors = new List<string>();
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
errors.Add($"{diagnostic.Id}: {diagnostic.GetMessage()}");
throw new InvalidOperationException(string.Join("\n", errors));
}
assemblyStream.Seek(0, SeekOrigin.Begin);
symbolsStream.Seek(0, SeekOrigin.Begin);
#if NET6_0_OR_GREATER
return System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromStream(assemblyStream, symbolsStream);
#else
return Assembly.Load(assemblyStream.ToArray(), symbolsStream.ToArray());
#endif
}
}
}