Skip to content

Commit 69744c2

Browse files
committed
Work with delegate catalog
1 parent 775cbd9 commit 69744c2

File tree

9 files changed

+540
-267
lines changed

9 files changed

+540
-267
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.Immutable;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Reflection;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp;
9+
10+
namespace Weikio.PluginFramework.Catalogs.Delegates
11+
{
12+
/// <summary>
13+
/// Note: Heavily based on the work done by Jeremy Miller in Lamar: https://github.com/jasperfx/Lamar
14+
/// </summary>
15+
public class CodeToAssemblyGenerator
16+
{
17+
private readonly IList<Assembly> _assemblies = new List<Assembly>();
18+
private readonly IList<MetadataReference> _references = new List<MetadataReference>();
19+
20+
public CodeToAssemblyGenerator()
21+
{
22+
ReferenceAssemblyContainingType<object>();
23+
ReferenceAssembly(typeof(Enumerable).GetTypeInfo().Assembly);
24+
}
25+
26+
public string AssemblyName { get; set; }
27+
28+
public void ReferenceAssembly(Assembly assembly)
29+
{
30+
if (assembly == null || _assemblies.Contains(assembly))
31+
{
32+
return;
33+
}
34+
35+
_assemblies.Add(assembly);
36+
37+
try
38+
{
39+
var referencePath = CreateAssemblyReference(assembly);
40+
41+
if (referencePath == null)
42+
{
43+
Console.WriteLine("Could not make an assembly reference to " + assembly.FullName);
44+
}
45+
else
46+
{
47+
if (_references.Any(
48+
x => x.Display == referencePath))
49+
{
50+
return;
51+
}
52+
53+
_references.Add(MetadataReference.CreateFromFile(referencePath));
54+
55+
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
56+
{
57+
ReferenceAssembly(Assembly.Load(referencedAssembly));
58+
}
59+
}
60+
}
61+
catch (Exception ex)
62+
{
63+
Console.WriteLine($"Could not make an assembly reference to {assembly.FullName}\n\n{ex}");
64+
}
65+
}
66+
67+
private static string CreateAssemblyReference(Assembly assembly)
68+
{
69+
if (assembly.IsDynamic)
70+
{
71+
return null;
72+
}
73+
74+
return assembly.Location;
75+
}
76+
77+
public void ReferenceAssemblyContainingType<T>()
78+
{
79+
ReferenceAssembly(typeof(T).GetTypeInfo().Assembly);
80+
}
81+
82+
public Assembly Generate(string code)
83+
{
84+
var str = AssemblyName ?? Path.GetRandomFileName();
85+
var text = CSharpSyntaxTree.ParseText(code);
86+
var array = _references.ToArray();
87+
var syntaxTreeArray = new SyntaxTree[1] { text };
88+
89+
using (var memoryStream = new MemoryStream())
90+
{
91+
var emitResult = CSharpCompilation
92+
.Create(str, syntaxTreeArray, array,
93+
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, false, null,
94+
null, null, null, OptimizationLevel.Debug, false,
95+
false, null, null, new ImmutableArray<byte>(), new bool?())).Emit(memoryStream);
96+
97+
if (!emitResult.Success)
98+
{
99+
var errors = emitResult.Diagnostics
100+
.Where(diagnostic =>
101+
diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
102+
103+
var errorsMsg = string.Join("\n", errors.Select(x => x.Id + ": " + x.GetMessage()));
104+
105+
var errorMsgWithCode = "Compilation failures!" + Environment.NewLine + Environment.NewLine +
106+
errorsMsg + Environment.NewLine + Environment.NewLine + "Code:" +
107+
Environment.NewLine + Environment.NewLine + code;
108+
109+
throw new InvalidOperationException(errorMsgWithCode);
110+
}
111+
112+
memoryStream.Seek(0L, SeekOrigin.Begin);
113+
114+
var c = new CustomAssemblyLoadContext();
115+
116+
var assembly = c.LoadFromStream(memoryStream);
117+
118+
return assembly;
119+
}
120+
}
121+
}
122+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Reflection;
2+
using System.Runtime.Loader;
3+
4+
namespace Weikio.PluginFramework.Catalogs.Delegates
5+
{
6+
public sealed class CustomAssemblyLoadContext : AssemblyLoadContext
7+
{
8+
protected override Assembly Load(AssemblyName assemblyName)
9+
{
10+
return Assembly.Load(assemblyName);
11+
}
12+
}
13+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Weikio.PluginFramework.Catalogs.Delegates
5+
{
6+
public class DelegateCache
7+
{
8+
public static Dictionary<Guid, MulticastDelegate> Cache = new Dictionary<Guid, MulticastDelegate>();
9+
10+
public static Guid Add(MulticastDelegate multicastDelegate)
11+
{
12+
var id = Guid.NewGuid();
13+
Cache.Add(id, multicastDelegate);
14+
15+
return id;
16+
}
17+
18+
public static MulticastDelegate Get(Guid id)
19+
{
20+
return Cache[id];
21+
}
22+
}
23+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
using System.Threading.Tasks;
5+
using Weikio.PluginFramework.Abstractions;
6+
7+
namespace Weikio.PluginFramework.Catalogs.Delegates
8+
{
9+
public class DelegatePluginCatalog : IPluginCatalog
10+
{
11+
private AssemblyPluginCatalog _catalog;
12+
private readonly MulticastDelegate _multicastDelegate;
13+
private readonly List<(Predicate<ParameterInfo>, Func<ParameterInfo, ParameterConversion>)> _conversionRules;
14+
private readonly PluginNameOptions _nameOptions;
15+
16+
public DelegatePluginCatalog(MulticastDelegate multicastDelegate, List<(Predicate<ParameterInfo>, Func<ParameterInfo, ParameterConversion>)> conversionRules = null,
17+
PluginNameOptions nameOptions = null)
18+
{
19+
if (multicastDelegate == null)
20+
{
21+
throw new ArgumentNullException(nameof(multicastDelegate));
22+
}
23+
24+
_multicastDelegate = multicastDelegate;
25+
26+
if (conversionRules == null)
27+
{
28+
conversionRules = new List<(Predicate<ParameterInfo>, Func<ParameterInfo, ParameterConversion>)>();
29+
}
30+
31+
_conversionRules = conversionRules;
32+
33+
if (nameOptions == null)
34+
{
35+
nameOptions = new PluginNameOptions();
36+
}
37+
38+
_nameOptions = nameOptions;
39+
}
40+
41+
public async Task Initialize()
42+
{
43+
var converter = new DelegateToAssemblyConverter();
44+
var assembly = converter.CreateAssembly(_multicastDelegate, _conversionRules);
45+
46+
var options = new AssemblyPluginCatalogOptions()
47+
{
48+
PluginNameOptions = _nameOptions
49+
};
50+
51+
_catalog = new AssemblyPluginCatalog(assembly, options);
52+
await _catalog.Initialize();
53+
54+
IsInitialized = true;
55+
}
56+
57+
public bool IsInitialized { get; set; }
58+
public List<Plugin> GetPlugins()
59+
{
60+
return _catalog.GetPlugins();
61+
}
62+
63+
public Plugin Get(string name, Version version)
64+
{
65+
return _catalog.Get(name, version);
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)