Skip to content

Commit

Permalink
支持编译单元隔离
Browse files Browse the repository at this point in the history
1. 编译单元增加 UseRandomDomain 、UseNewDomain 、 UseDefaultDomain  方法.
2. 编译单元增加 GetSingleReference 方法支持查询引用.
3. 编译单元增加 AddReference 以支持根据程序集添加引用和 Using.
4. 编译选项增加 AppendCompilerFlag 绑定编译标识.
  • Loading branch information
NMSAzulX committed Nov 20, 2023
1 parent 0093dc2 commit 02f3b0f
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 54 deletions.
56 changes: 50 additions & 6 deletions samples/ReferenceSample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,54 @@ internal class Program
{
static void Main(string[] args)
{
Run();
//Run();
NatashaManagement.Preheating(true, true);
GC.Collect();
Thread.Sleep(15000);
for (int i = 0; i < 5; i++)
{
TestMini();
Thread.Sleep(3000);
}
Console.ReadKey();

}

public static void TestMini()
{
AssemblyCSharpBuilder builder = new AssemblyCSharpBuilder();
builder
.UseRandomDomain()
.ConfigCompilerOption(opt=>opt
.AppendCompilerFlag(
Natasha.CSharp.Compiler.CompilerBinderFlags.SuppressConstraintChecks |
Natasha.CSharp.Compiler.CompilerBinderFlags.SuppressObsoleteChecks |
Natasha.CSharp.Compiler.CompilerBinderFlags.SuppressTypeArgumentBinding |
Natasha.CSharp.Compiler.CompilerBinderFlags.SuppressUnsafeDiagnostics))
.DisableSemanticCheck()
.AddReference(typeof(object).Assembly)
.AddReference(typeof(Math).Assembly)
.AddReference(typeof(MathF).Assembly)
.ConfigReferenceCombineBehavior(CombineReferenceBehavior.UseCurrent);

builder.Add(@"public static class A{
public static int N1 = 10;
public static float N2 = 1.2F;
public static double N3 = 3.44;
public static object Invoke(){
return N1 + MathF.Log10((float)Math.Sqrt(MathF.Sqrt(N2) + Math.Tan(N3)));
}
}", UsingLoadBehavior.WithCurrent);

var asm = builder.GetAssembly();
var type = asm.GetType("A");
var method = type.GetMethod("Invoke");
var result = method.Invoke(null, null);
Console.WriteLine(result);
}

[MethodImpl(MethodImplOptions.NoInlining)]
public static void Run()
{
Expand All @@ -30,7 +74,7 @@ public static void Run1()
builder.Domain = DomainManagement.Random();


builder.AddWithDefaultUsing("public class A { public string Name {get;set;} = \"abc\"; }");
builder.Add("public class A { public string Name {get;set;} = \"abc\"; }", UsingLoadBehavior.WithDefault);
//此API可以在不用编译的情况下获取到已经格式化好的语法树.
//注: GetAssembly 方法中已包括此方法, 但不会重复运行.
builder.GetAvailableCompilation();
Expand All @@ -45,9 +89,9 @@ public static void Run1()
builder.WithRandomAssenblyName();
builder.ClearScript();
//向 builder 中追加类型 B.
builder.AddWithDefaultUsing("namespace TempA { public class A { public string Name {get;set;} = \"abcAaaa\"; }}");
builder.AddWithDefaultUsing("public static class B{ public static void Show(){ Console.WriteLine(\"HelloWorld!\" + (new TempA.A()).Name); } }");
builder.AddWithDefaultUsing("public class C: A { public string Name {get;set;} }");
builder.Add("namespace TempA { public class A { public string Name {get;set;} = \"abcAaaa\"; }}", UsingLoadBehavior.WithDefault);
builder.Add("public static class B{ public static void Show(){ Console.WriteLine(\"HelloWorld!\" + (new TempA.A()).Name); } }", UsingLoadBehavior.WithDefault);
builder.Add("public class C: A { public string Name {get;set;} }", UsingLoadBehavior.WithDefault);
var assemblyAB = builder.GetAssembly();
//Console.WriteLine(assemblyAB == assemblyA);
//var a = assemblyAB.GetName().GetUniqueName();
Expand All @@ -60,7 +104,7 @@ public static void Run1()

builder.Clear();
builder.WithRandomAssenblyName();
builder.AddWithDefaultUsing("public static class D{ public static object Show(){ return new A(); } }");
builder.Add("public static class D{ public static object Show(){ return new A(); } }", UsingLoadBehavior.WithDefault);
var assemblyD = builder.GetAssembly();
var func = assemblyD.GetDelegateFromShortName<Func<object>>("D", "Show");
dynamic obj = func();
Expand Down
2 changes: 1 addition & 1 deletion samples/ReferenceSample/ReferenceSample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Natasha.CSharp\Natasha.CSharp\Natasha.CSharp.csproj" />
<ProjectReference Include="..\..\src\Natasha.CSharp\Natasha.CSharp.Compiler\Natasha.CSharp.Compiler.csproj" />
</ItemGroup>

<!--<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Emit;
using System.Reflection.Metadata;



#if NETCOREAPP3_0_OR_GREATER
using System.IO;
Expand Down Expand Up @@ -163,13 +167,15 @@ public CSharpCompilation GetAvailableCompilation(Func<CSharpCompilationOptions,
/// </code>
/// </example>
/// </remarks>
public Assembly GetAssembly(Assembly? currentAssembly = null)
public Assembly GetAssembly()
{

#if DEBUG
Stopwatch stopwatch = new();
stopwatch.Start();
#endif


Stream dllStream;
Stream pdbStream;
Stream? xmlStream = null;
Expand All @@ -196,20 +202,12 @@ public Assembly GetAssembly(Assembly? currentAssembly = null)
xmlStream = File.Create(XmlFilePath);
}

if (currentAssembly != null)
{
_compilation =
GetAvailableCompilation(opt=>opt
.WithModuleName(AssemblyName)
.WithOutputKind(OutputKind.NetModule));

}
else if (_compilation == null)
if (_compilation == null)
{
_compilation = GetAvailableCompilation();
}


var compileResult = _compilation.Emit(
dllStream,
pdbStream: pdbStream,
Expand All @@ -218,24 +216,13 @@ public Assembly GetAssembly(Assembly? currentAssembly = null)


LogCompilationEvent?.Invoke(_compilation.GetNatashaLog());

Assembly? assembly = currentAssembly;
Assembly? assembly = null;
if (compileResult.Success)
{
dllStream.Seek(0, SeekOrigin.Begin);
if (assembly == null)
{

pdbStream?.Seek(0, SeekOrigin.Begin);
Domain.SetAssemblyLoadBehavior(_compileAssemblyBehavior);
assembly = Domain.LoadAssemblyFromStream(dllStream, pdbStream);
}
else
{
byte[] rawStream = new byte[dllStream.Length];
dllStream.Read(rawStream.AsSpan());
assembly.LoadModule(AssemblyName, rawStream);
}
pdbStream?.Seek(0, SeekOrigin.Begin);
Domain.SetAssemblyLoadBehavior(_compileAssemblyBehavior);
assembly = Domain.LoadAssemblyFromStream(dllStream, pdbStream);
CompileSucceedEvent?.Invoke(_compilation, assembly!);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public sealed partial class AssemblyCSharpBuilder
/// <param name="script">脚本代码</param>
/// <param name="usingLoadBehavior">using 拼接行为</param>
/// <returns></returns>
public AssemblyCSharpBuilder Add(string script, UsingLoadBehavior usingLoadBehavior)
public AssemblyCSharpBuilder Add(string script, UsingLoadBehavior usingLoadBehavior = UsingLoadBehavior.WithCurrent)
{
switch (usingLoadBehavior)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Text;
Expand All @@ -30,7 +31,30 @@ public NatashaReferenceCache()
_referenceNameCache = new();
}

/// <summary>
/// 根据程序集名获取引用
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public MetadataReference? GetSingleReference(AssemblyName name)
{
if (_referenceNameCache.TryGetValue(name.GetUniqueName(),out var realName))
{
return _referenceCache[realName];
}
return null;
}

public int Count { get { return _referenceCache.Count; } }

public unsafe void AddReference(Assembly assembly, PluginLoadBehavior loadReferenceBehavior = PluginLoadBehavior.None)
{
if (assembly.TryGetRawMetadata(out var blob, out var length))
{
var metadataReference = AssemblyMetadata.Create(ModuleMetadata.CreateFromMetadata((IntPtr)blob, length)).GetReference();
AddReference(assembly.GetName(), metadataReference, loadReferenceBehavior);
}
}
public void AddReference(AssemblyName assemblyName, MetadataReference reference, PluginLoadBehavior loadReferenceBehavior)
{

Expand Down Expand Up @@ -63,13 +87,6 @@ public void AddReference(AssemblyName assemblyName, string path, PluginLoadBehav
AddReference(assemblyName, CreateMetadataReference(path), loadReferenceBehavior);
}

public void AddReference(Assembly assembly, PluginLoadBehavior loadReferenceBehavior = PluginLoadBehavior.None)
{
if (!assembly.IsDynamic && !string.IsNullOrEmpty(assembly.Location))
{
AddReference(assembly.GetName(), CreateMetadataReference(assembly.Location), loadReferenceBehavior);
}
}
public void RemoveReference(AssemblyName assemblyName)
{
_referenceCache!.Remove(assemblyName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#if NETCOREAPP3_0_OR_GREATER
using System.Reflection;
using System.Xml.Linq;


public static class NatashaAssemblyBuilderMultiExtension
{
/// <summary>
/// 编译单元使用随机域
/// </summary>
/// <param name="builder">编译单元</param>
/// <returns></returns>
public static AssemblyCSharpBuilder UseRandomDomain(this AssemblyCSharpBuilder builder)
{
builder.Domain = DomainManagement.Random();
return builder;
}

/// <summary>
/// 编译单元使用新的随机域
/// </summary>
/// <param name="builder">编译单元</param>
/// <param name="domainName">域名字</param>
/// <returns></returns>
public static AssemblyCSharpBuilder UseNewDomain(this AssemblyCSharpBuilder builder,string domainName)
{
builder.Domain = DomainManagement.Create(domainName);
return builder;
}

/// <summary>
/// 编译单元使用新的随机域
/// </summary>
/// <param name="builder">编译单元</param>
/// <returns></returns>
public static AssemblyCSharpBuilder UseDefaultDomain(this AssemblyCSharpBuilder builder)
{
builder.Domain = NatashaReferenceDomain.DefaultDomain;
return builder;
}

/// <summary>
/// 增加引用 和 using
/// </summary>
/// <param name="builder">natasha 编译单元</param>
/// <param name="assembly">程序集</param>
/// <param name="loadReferenceBehavior">加载行为</param>
public static AssemblyCSharpBuilder AddReference(this AssemblyCSharpBuilder builder, Assembly assembly, PluginLoadBehavior loadReferenceBehavior = PluginLoadBehavior.None)
{
var name = assembly.GetName();
var domain = builder.Domain;
var reference = domain.References.GetSingleReference(name);
if (reference == null)
{
domain.References.AddReference(assembly);
domain.UsingRecorder.Using(assembly);
}
return builder;
}
/// <summary>
/// 增加引用 和 using
/// </summary>
/// <param name="builder">natasha 编译单元</param>
/// <param name="path">文件路径</param>
/// <param name="loadReferenceBehavior">加载行为</param>
public static AssemblyCSharpBuilder AddReference(this AssemblyCSharpBuilder builder, string path, PluginLoadBehavior loadReferenceBehavior = PluginLoadBehavior.None)
{
var resolver = new PathAssemblyResolver([path]);
using var mlc = new MetadataLoadContext(resolver);
Assembly assembly = mlc.LoadFromAssemblyPath(path);
var name = assembly.GetName();
var domain = builder.Domain;
var reference = domain.References.GetSingleReference(name);
if (reference == null)
{
domain.References.AddReference(assembly.GetName(), path);
domain.UsingRecorder.Using(assembly);
}
return builder;
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,11 @@ public static void Show(Assembly assembly)
#endif


private unsafe static ParallelLoopResult InitReferenceFromRuntime(Assembly[] assemblies)
private static ParallelLoopResult InitReferenceFromRuntime(Assembly[] assemblies)
{
return Parallel.ForEach(assemblies, assembly =>
{
if (assembly.TryGetRawMetadata(out var blob, out var length))
{
var metadata = AssemblyMetadata.Create(ModuleMetadata.CreateFromMetadata((IntPtr)blob, length));
var metadataReference = metadata.GetReference();
NatashaReferenceDomain.DefaultDomain.References.AddReference(assembly.GetName(), metadataReference, PluginLoadBehavior.None);
}
NatashaReferenceDomain.DefaultDomain.References.AddReference(assembly);
});
}
//*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ public static NatashaReferenceDomain CreateRandomDomain()
/// <returns></returns>
public static bool AddGlobalReference(Type type, PluginLoadBehavior loadBehavior = PluginLoadBehavior.None)
{
if (type.Assembly.IsDynamic || type.Assembly.Location == null)
{
return false;
}
NatashaReferenceDomain.DefaultDomain.References.AddReference(type.Assembly, loadBehavior);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,20 @@ public NatashaCSharpCompilerOptions SetCompilerFlag(CompilerBinderFlags flags)
return this;
}

/// <summary>
/// 增加编译标识
/// </summary>
/// <param name="flags"></param>
/// <returns></returns>
public NatashaCSharpCompilerOptions AppendCompilerFlag(params CompilerBinderFlags[] flags)
{
for (int i = 0; i < flags.Length; i++)
{
_compileFlags |= flags[i];
}
return this;
}

/// <summary>
/// 移除 IgnoreAccessibility 标识
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public NatashaUsingCache Using(Assembly assembly)
{
try
{
Using(assembly.GetTypes());
Using(assembly.GetExportedTypes());
}
catch
{
Expand Down
4 changes: 2 additions & 2 deletions src/Natasha.CSharp/Natasha.CSharp/Public/NatashaManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

public static partial class NatashaManagement
{
#if MULTI
#if NETCOREAPP3_0_OR_GREATER
/// <summary>
/// 和 NatashaInitializer.Preheating(); 一样
/// </summary>
Expand All @@ -21,7 +21,7 @@ public static void Preheating(
NatashaInitializer.Preheating(null, useRuntimeUsing, useRuntimeReference);
}
#else
public static void Preheating(
public static void Preheating(
Func<AssemblyName, string?, bool>? excludeReferencesFunc = null)
{
NatashaInitializer.Preheating(excludeReferencesFunc);
Expand Down

0 comments on commit 02f3b0f

Please sign in to comment.