Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

增加和优化部分方法注释 #299

Merged
merged 1 commit into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.Extensions.DependencyModel;
using Natasha.CSharp.Compiler.Component;
using Natasha.CSharp.Compiler.Component.Exception;
using Natasha.CSharp.Extension.Inner;
Expand All @@ -12,7 +11,6 @@
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
/// <summary>
/// 程序集编译构建器 - 编译选项
/// </summary>
Expand All @@ -24,21 +22,41 @@ public sealed partial class AssemblyCSharpBuilder


/// <summary>
/// 编译时,使用主域引用覆盖引用集,并配置同名引用版本行为(默认优先使用主域引用)
/// 该方法允许共享域参与编译.
/// <list type="bullet">
/// <item>
/// <description>[共享域] 元数据参与编译.</description>
/// </item>
/// <item>
/// <description>[当前域] 元数据参与编译.</description>
/// </item>
/// </list>
/// </summary>
/// <param name="action">配置委托</param>
/// <returns></returns>
/// <remarks>
/// 注:若两个域不同,且存在相同名称元数据,默认优先使用主域的元数据.
/// </remarks>
/// <param name="action">配置同名元数据的解决策略</param>
/// <returns>链式对象(调用方法的实例本身).</returns>
public AssemblyCSharpBuilder WithCombineReferences(Action<ReferenceConfiguration>? action = null)
{
action?.Invoke(_referenceConfiguration);
_combineReferenceBehavior = CombineReferenceBehavior.CombineDefault;
return this;
}


/// <summary>
/// 编译时,使用当前域引用来覆盖引用集
/// 配置编译元数据的合并行为.
/// <list type="bullet">
/// <item>
/// <description>[共享域] 元数据 [不] 参与编译.</description>
/// </item>
/// <item>
/// <description>[当前域] 元数据参与编译.</description>
/// </item>
/// </list>
/// </summary>
/// <returns></returns>
/// <returns>链式对象(调用方法的实例本身).</returns>
public AssemblyCSharpBuilder WithCurrentReferences()
{
_combineReferenceBehavior = CombineReferenceBehavior.UseCurrent;
Expand All @@ -47,10 +65,21 @@ public AssemblyCSharpBuilder WithCurrentReferences()

private readonly List<MetadataReference> _specifiedReferences;
/// <summary>
/// 使用外部指定的引用来覆盖引用集
/// 使用外部指定的元数据引用进行编译.
/// <list type="bullet">
/// <item>
/// <description>[共享域] 元数据 [不] 参与编译.</description>
/// </item>
/// <item>
/// <description>[当前域] 元数据 [不] 参与编译.</description>
/// </item>
/// </list>
/// </summary>
/// <remarks>
/// 使用 ClearOutsideReferences 可以清楚本次传递的元数据引用.
/// </remarks>
/// <param name="metadataReferences"></param>
/// <returns></returns>
/// <returns>链式对象(调用方法的实例本身).</returns>
public AssemblyCSharpBuilder WithSpecifiedReferences(IEnumerable<MetadataReference> metadataReferences)
{
lock (_specifiedReferences)
Expand All @@ -60,6 +89,11 @@ public AssemblyCSharpBuilder WithSpecifiedReferences(IEnumerable<MetadataReferen
_combineReferenceBehavior = CombineReferenceBehavior.UseSpecified;
return this;
}

/// <summary>
/// 清除由 WithSpecifiedReferences 方法传入的元数据引用.
/// </summary>
/// <returns>链式对象(调用方法的实例本身).</returns>
public AssemblyCSharpBuilder ClearOutsideReferences()
{
lock (_specifiedReferences)
Expand All @@ -71,36 +105,50 @@ public AssemblyCSharpBuilder ClearOutsideReferences()


/// <summary>
/// 配置引用过滤策略
/// 配置元数据引用过滤策略.
/// </summary>
/// <param name="referencesFilter"></param>
/// <returns></returns>
/// <returns>链式对象(调用方法的实例本身).</returns>
public AssemblyCSharpBuilder SetReferencesFilter(Func<IEnumerable<MetadataReference>, IEnumerable<MetadataReference>>? referencesFilter)
{
_referencesFilter = referencesFilter;
return this;
}

/// <summary>
/// 流编译成功之后触发的事件
/// 流编译成功之后触发的事件.
/// </summary>
/// <remarks>
/// 此时已编译结束,程序集已经生成并加载.
/// </remarks>
public event Action<CSharpCompilation, Assembly>? CompileSucceedEvent;



/// <summary>
/// 流编译失败之后触发的事件
/// 流编译失败之后触发的事件.
/// </summary>
/// <remarks>
/// 此时已经编译结束, 但是编译失败.
/// </remarks>
public event Action<CSharpCompilation, ImmutableArray<Diagnostic>>? CompileFailedEvent;



private ConcurrentQueue<Func<EmitOptions, EmitOptions>>? _emitOptionHandle;
/// <summary>
/// 处理默认生成的 emitOption 并返回一个新的
/// 追加对 emitOption 的处理逻辑.
/// <list type="bullet">
/// <item>一次性配置,不可重用.</item>
/// <item>多次调用会进入配置队列.</item>
/// <item>调用 <see cref="GetAssembly"/> 后清空队列.</item>
/// <item>调用 <see cref="Clear"/> 后清空队列.</item>
/// <item>调用 <see cref="ClearEmitOptionCache"/> 后清空队列.</item>
/// </list>
/// </summary>
/// <remarks>
/// 注:该配置属于一次性配置,若重复使用该配置逻辑,请在这次编译后重新调用该方法.
/// </remarks>
/// <param name="handleAndReturnNewEmitOption"></param>
/// <returns></returns>
/// <returns>链式对象(调用方法的实例本身).</returns>
public AssemblyCSharpBuilder ConfigEmitOptions(Func<EmitOptions, EmitOptions> handleAndReturnNewEmitOption)
{
_emitOptionHandle ??= new ConcurrentQueue<Func<EmitOptions, EmitOptions>>();
Expand All @@ -110,55 +158,61 @@ public AssemblyCSharpBuilder ConfigEmitOptions(Func<EmitOptions, EmitOptions> ha

private bool _notLoadIntoDomain;
/// <summary>
/// 仅仅生成程序集,而不加载到域
/// 仅仅生成程序集,而不加载到域.
/// </summary>
/// <returns></returns>
/// <returns>链式对象(调用方法的实例本身).</returns>
public AssemblyCSharpBuilder WithoutInjectToDomain()
{
_notLoadIntoDomain = true;
return this;
}
/// <summary>
/// 既生成程序集又加载到域
/// 既生成程序集又加载到域(不用配置,默认行为).
/// </summary>
/// <returns></returns>
/// <returns>链式对象(调用方法的实例本身).</returns>
public AssemblyCSharpBuilder WithInjectToDomain()
{
_notLoadIntoDomain = false;
return this;
}

/// <summary>
/// 将 SyntaxTrees 中的语法树编译到程序集.如果不成功会抛出 NatashaException.
/// </summary>
/// <remarks>
/// <example>
/// 编译并获取程序集.
/// <list type="number">
/// <item> 编译后的信息获取
/// <list type="bullet">
/// <item>用 <see cref="Compilation"/> 获取编译配置载体.</item>
/// <item>用 <see cref="GetDiagnostics"/> 获取诊断结果.</item>
/// <item>用 <see cref="GetException"/> 获取运行抛出的异常结果.</item>
/// <item>用 <see cref="CompileSucceedEvent"/> 监听成功编译结果.</item>
/// <item>用 <see cref="CompileFailedEvent"/> 监听失败编译结果.</item>
/// <code>
///
/// //Core3.0以上版本
/// //程序集的域加载行为决定了编译后的程序集随着依赖加载到域中的处理结果.
/// //编译单元支持的依赖加载方法:
/// WithHighVersionDependency
/// WithLowVersionDependency
/// WithDefaultVersionDependency
/// WithCustomVersionDependency
///
/// //编译单元的引用加载行为, 遇到同名不同版本的引用该如何处理.
/// //首先启用合并引用,此方法将允许主域引用和当前域引用进行整合
/// builder.WithCombineReferences(configAction);
/// //其参数支持同名引用的加载逻辑,包括
/// config.UseHighVersionReferences
/// config.UseLowVersionReferences
/// config.UseDefaultReferences
/// config.UseCustomReferences
/// //手动设置同名过滤器
/// config.ConfigSameNameReferencesFilter(func);
/// //手动设置全部引用过滤器
/// builder.SetReferencesFilter
///
/// builder.CompileFailedEvent += (compilation, errors) =>{
/// log = compilation.GetNatashaLog();
/// log.ToString();
/// };
/// </code>
/// </example>
/// <item>用 <see cref="LogCompilationEvent"/> 监听编译日志.
/// <code>
/// builder.LogCompilationEvent += (log) =>{
/// //log.ToString();
/// };
/// </code>
/// </item>
/// </list>
/// </item>
/// <item>重复编译
/// <list type="bullet">
/// <item>查看您所使用过的 Config 开头方法的注释.</item>
/// <item>用 <see cref="Clear"/> 方法使 Builder 可以继续使用.</item>
/// </list>
/// </item>
/// </list>
/// </summary>
/// <remarks>
/// 注:若不需要加载到域,需提前使用 WithoutInjectToDomain 方法.
/// </remarks>
/// <returns>编译成功生成的程序集.</returns>
public Assembly GetAssembly()
{
_compilation ??= GetAvailableCompilation();
Expand All @@ -167,8 +221,6 @@ public Assembly GetAssembly()
Domain.SetAssemblyLoadBehavior(_domainConfiguration._dependencyLoadBehavior);
}



#if DEBUG
Stopwatch stopwatch = new();
stopwatch.Start();
Expand Down Expand Up @@ -235,8 +287,7 @@ public Assembly GetAssembly()
includePrivateMembers: _includePrivateMembers,
metadataOnly: _isReferenceAssembly,
pdbFilePath: PdbFilePath,
debugInformationFormat: debugInfoFormat
);
debugInformationFormat: debugInfoFormat);

if (_emitOptionHandle != null)
{
Expand Down Expand Up @@ -282,7 +333,8 @@ public Assembly GetAssembly()
else
{
CompileFailedEvent?.Invoke(_compilation, compileResult.Diagnostics);
throw NatashaExceptionAnalyzer.GetCompileException(_compilation, compileResult.Diagnostics);
_exception = NatashaExceptionAnalyzer.GetCompileException(_compilation, compileResult.Diagnostics);
throw _exception;
}
dllStream.Dispose();
pdbStream?.Dispose();
Expand All @@ -294,8 +346,12 @@ public Assembly GetAssembly()
return assembly!;
}


public unsafe (Assembly, byte[], byte[], byte[]) UpdateAssembly(Assembly oldAssembly)
/// <summary>
/// 热重载相关(未完成,无法使用)
/// </summary>
/// <param name="oldAssembly"></param>
/// <returns></returns>
public unsafe Assembly UpdateAssembly(Assembly oldAssembly)
{
_compilation ??= GetAvailableCompilation();
if (Domain!.Name != "Default")
Expand Down Expand Up @@ -388,24 +444,24 @@ public unsafe (Assembly, byte[], byte[], byte[]) UpdateAssembly(Assembly oldAsse
var ilDelta = AsReadOnlySpan(dllStream);
var pdbDelta = AsReadOnlySpan(pdbStream);
var metadataDelta = AsReadOnlySpan(metaStream);
RuntimeInnerHelper.ApplyUpdate(oldAssembly, metadataDelta, ilDelta, pdbDelta);
return (oldAssembly!, metadataDelta.ToArray(), ilDelta.ToArray(), pdbDelta.ToArray());
RuntimeInnerHelper.ApplyUpdate(oldAssembly, metadataDelta, ilDelta, null);
dllStream.Dispose();
pdbStream.Dispose();
metaStream.Dispose();
xmlStream?.Dispose();
return oldAssembly!;

}
else
{
dllStream.Dispose();
pdbStream.Dispose();
metaStream.Dispose();
xmlStream?.Dispose();
CompileFailedEvent?.Invoke(_compilation, compileResult.Diagnostics);
throw NatashaExceptionAnalyzer.GetCompileException(_compilation, compileResult.Diagnostics);
_exception = NatashaExceptionAnalyzer.GetCompileException(_compilation, compileResult.Diagnostics);
throw _exception;
}
dllStream.Dispose();
pdbStream.Dispose();
metaStream.Dispose();
xmlStream?.Dispose();

#if DEBUG
stopwatch.StopAndShowCategoreInfo("[ Emit ]", "编译时长", 2);
#endif
return default!;

static ReadOnlySpan<byte> AsReadOnlySpan(Stream input)
{
Expand Down
Loading
Loading