From d6ef2b5e76f7bd364e804f4cd1e7dd47d278290f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=9C=A8=E6=A3=AE=20=C2=B7=20=E4=BD=9C=E9=9C=96?= <16236903+NMSAzulX@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:21:35 +0800 Subject: [PATCH] =?UTF-8?q?udpate=20compiler=20=E5=88=86=E7=A6=BB=E6=96=87?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E5=81=A5=E5=A3=AE=E7=A8=8B=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AssemblyCSharpBuilder.Compile.cs | 371 ++---------------- .../CompileUnit/AssemblyCSharpBuilder.Emit.cs | 31 ++ .../AssemblyCSharpBuilder.Event.cs | 36 ++ .../CompileUnit/AssemblyCSharpBuilder.Log.cs | 7 - .../AssemblyCSharpBuilder.Ouput.cs | 40 +- .../AssemblyCSharpBuilder.References.cs | 106 +++++ .../SemanticAnalaysis/UsingAnalysistor.cs | 4 +- .../Compiler/Utils/RuntimeInnerHelper.cs | 2 +- .../Extension/NatashaDiagnosticsExtension.cs | 2 +- .../Utils/NatashaFileRepeateHelper.cs | 21 + 10 files changed, 271 insertions(+), 349 deletions(-) create mode 100644 src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Emit.cs create mode 100644 src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Event.cs create mode 100644 src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.References.cs create mode 100644 src/Natasha.CSharp/Natasha.CSharp.Compiler/Utils/NatashaFileRepeateHelper.cs diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Compile.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Compile.cs index 84f004a8..ddc6da0d 100644 --- a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Compile.cs +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Compile.cs @@ -1,171 +1,54 @@ using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Emit; -using Natasha.CSharp.Compiler.Component; using Natasha.CSharp.Compiler.Component.Exception; using Natasha.CSharp.Compiler.Utils; using Natasha.CSharp.Extension.Inner; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Reflection; using System.Security.Cryptography; /// -/// 程序集编译构建器 - 编译选项 +/// 程序集编译构建器 - 编译器 /// public sealed partial class AssemblyCSharpBuilder { - private Func, IEnumerable>? _referencesFilter; - private CombineReferenceBehavior _combineReferenceBehavior = CombineReferenceBehavior.UseCurrent; - private readonly ReferenceConfiguration _referenceConfiguration = new(); - - - /// - /// 该方法允许共享域参与编译. - /// - /// - /// [共享域] 元数据参与编译. - /// - /// - /// [当前域] 元数据参与编译. - /// - /// - /// - /// - /// 注:若两个域不同,且存在相同名称元数据,默认优先使用主域的元数据. - /// - /// 配置同名元数据的解决策略 - /// 链式对象(调用方法的实例本身). - public AssemblyCSharpBuilder WithCombineReferences(Action? action = null) - { - action?.Invoke(_referenceConfiguration); - _combineReferenceBehavior = CombineReferenceBehavior.CombineDefault; - return this; - } - - - /// - /// 配置编译元数据的合并行为. - /// - /// - /// [共享域] 元数据 [不] 参与编译. - /// - /// - /// [当前域] 元数据参与编译. - /// - /// - /// - /// 链式对象(调用方法的实例本身). - public AssemblyCSharpBuilder WithCurrentReferences() - { - _combineReferenceBehavior = CombineReferenceBehavior.UseCurrent; - return this; - } - - private readonly List _specifiedReferences; - /// - /// 使用外部指定的元数据引用进行编译. - /// - /// - /// [共享域] 元数据 [不] 参与编译. - /// - /// - /// [当前域] 元数据 [不] 参与编译. - /// - /// - /// - /// - /// 使用 ClearOutsideReferences 可以清除本次传递的元数据引用. - /// - /// - /// 链式对象(调用方法的实例本身). - public AssemblyCSharpBuilder WithSpecifiedReferences(IEnumerable metadataReferences) - { - lock (_specifiedReferences) - { - _specifiedReferences.AddRange(metadataReferences); - } - _combineReferenceBehavior = CombineReferenceBehavior.UseSpecified; - return this; - } - - /// - /// 清除由 WithSpecifiedReferences 方法传入的元数据引用. - /// - /// 链式对象(调用方法的实例本身). - public AssemblyCSharpBuilder ClearOutsideReferences() - { - lock (_specifiedReferences) - { - _specifiedReferences.Clear(); - } - return this; - } - - - /// - /// 配置元数据引用过滤策略. - /// - /// - /// 链式对象(调用方法的实例本身). - public AssemblyCSharpBuilder SetReferencesFilter(Func, IEnumerable>? referencesFilter) - { - _referencesFilter = referencesFilter; - return this; - } - - /// - /// 流编译成功之后触发的事件. - /// - /// - /// 此时已编译结束,程序集已经生成并加载. - /// - public event Action? CompileSucceedEvent; - - - /// - /// 流编译失败之后触发的事件. - /// - /// - /// 此时已经编译结束, 但是编译失败. - /// - public event Action>? CompileFailedEvent; - - - private ConcurrentQueue>? _emitOptionHandle; /// - /// 追加对 emitOption 的处理逻辑. - /// - /// 一次性配置,不可重用. - /// 多次调用会进入配置队列. - /// 调用 后清空队列. - /// 调用 后清空队列. - /// 调用 后清空队列. + /// 重复编译 + /// + /// + /// 该方法逻辑: + /// + /// 用 WithPreCompilationOptions() 方法阻止创建新的 编译选项. + /// 用 WithPreCompilationReferences() 方法阻止覆盖新的引用. + /// + /// + /// + /// 提示: + /// + /// 若之前使用了 ConfigEmitOptions. 需要重新再写一遍. + /// 若已存在文件 a.dll,则生成 repeate.guid.a.dll. + /// WithForceCleanFile(); 可强制清除已存在文件. + /// WithoutForceCleanFile(); 则 a.dll 被换成 repeate.guid.a.dll. + /// 指定新的程序集名. + /// 若需要指定新的域. + /// + /// /// /// - /// - /// 注:该配置属于一次性配置,若重复使用该配置逻辑,请在这次编译后重新调用该方法. - /// - /// - /// 链式对象(调用方法的实例本身). - public AssemblyCSharpBuilder ConfigEmitOptions(Func handleAndReturnNewEmitOption) + /// + public AssemblyCSharpBuilder Reset() { - _emitOptionHandle ??= new ConcurrentQueue>(); - _emitOptionHandle.Enqueue(handleAndReturnNewEmitOption); + WithPreCompilationOptions(); + WithPreCompilationReferences(); return this; } /// /// 编译并获取程序集. - /// - /// 编译后的信息获取 /// /// 获取编译配置载体. /// 获取诊断结果. - /// 获取运行抛出的异常结果. + /// 获取抛出的异常结果. /// 监听成功编译结果. /// 监听失败编译结果. /// @@ -182,15 +65,6 @@ public AssemblyCSharpBuilder ConfigEmitOptions(Func ha /// /// /// - /// - /// 重复编译 - /// - /// 查看您所使用过的 Config 开头方法的注释. - /// 方法使 Builder 可以继续使用. - /// 方法清空上一次记录的输出路径. 如果第二编译需要输出,请重新指定路径. - /// - /// - /// /// public void CompileWithoutAssembly() { @@ -209,7 +83,8 @@ public void CompileWithoutAssembly() Stream? xmlStream = null; if (DllFilePath != string.Empty) { - dllStream = File.Create(DllFilePath); + dllStream = File.Create(FileHandle(DllFilePath)); + } else { @@ -218,7 +93,7 @@ public void CompileWithoutAssembly() if (CommentFilePath != string.Empty) { - xmlStream = File.Create(CommentFilePath); + xmlStream = File.Create(FileHandle(CommentFilePath)); } var debugInfoFormat = _debugConfiguration._informationFormat; @@ -238,16 +113,7 @@ public void CompileWithoutAssembly() Directory.CreateDirectory(tempPdbOutputFolder); } } - if (File.Exists(PdbFilePath)) - { - var tempPdbOutputFolder = Path.Combine(GlobalOutputFolder, Domain.Name!); - PdbFilePath = Path.Combine(tempPdbOutputFolder, $"repeate.{AssemblyName}.{Guid.NewGuid():N}.pdb"); - if (!Directory.Exists(tempPdbOutputFolder)) - { - Directory.CreateDirectory(tempPdbOutputFolder); - } - } - pdbStream = File.Create(PdbFilePath); + pdbStream = File.Create(FileHandle(PdbFilePath)); } else { @@ -306,15 +172,12 @@ public void CompileWithoutAssembly() #endif } - /// /// 编译并获取程序集. - /// - /// 编译后的信息获取 /// /// 获取编译配置载体. /// 获取诊断结果. - /// 获取运行抛出的异常结果. + /// 获取抛出的异常结果. /// 监听成功编译结果. /// 监听失败编译结果. /// @@ -331,15 +194,6 @@ public void CompileWithoutAssembly() /// /// /// - /// - /// 重复编译 - /// - /// 查看您所使用过的 Config 开头方法的注释. - /// 方法使 Builder 可以继续使用. - /// 方法清空上一次记录的输出路径. 如果第二编译需要输出,请重新指定路径. - /// - /// - /// /// /// /// 注:若不需要加载到域,请使用 CompileWithoutAssembly 方法. @@ -363,16 +217,15 @@ public Assembly GetAssembly() Stream? xmlStream = null; if (DllFilePath != string.Empty) { - dllStream = File.Create(DllFilePath); + dllStream = File.Create(FileHandle(DllFilePath)); } else { dllStream = new MemoryStream(); } - if (CommentFilePath != string.Empty) { - xmlStream = File.Create(CommentFilePath); + xmlStream = File.Create(FileHandle(CommentFilePath)); } var debugInfoFormat = _debugConfiguration._informationFormat; @@ -396,27 +249,8 @@ public Assembly GetAssembly() Directory.CreateDirectory(tempPdbOutputFolder); } } - if (File.Exists(PdbFilePath)) - { - var tempPdbOutputFolder = Path.Combine(GlobalOutputFolder, Domain.Name!); - PdbFilePath = Path.Combine(tempPdbOutputFolder, $"repeate.{AssemblyName}.{Guid.NewGuid():N}.pdb"); - if (!Directory.Exists(tempPdbOutputFolder)) - { - Directory.CreateDirectory(tempPdbOutputFolder); - } - } - pdbStream = File.Create(PdbFilePath); + pdbStream = File.Create(FileHandle(PdbFilePath)); } - //pdbStream = new MemoryStream(); - /* - if (debugInfoFormat != DebugInformationFormat.Embedded) - { - - } - else - { - PdbFilePath = null; - }*/ } else { @@ -459,10 +293,7 @@ public Assembly GetAssembly() if (compileResult.Success) { dllStream.Position = 0; - if (pdbStream != null) - { - pdbStream.Dispose(); - } + pdbStream?.Dispose(); assembly = Domain.LoadAssemblyFromStream(dllStream, null); LoadContext!.LoadMetadataWithAssembly(assembly); CompileSucceedEvent?.Invoke(_compilation, assembly!); @@ -482,136 +313,4 @@ public Assembly GetAssembly() #endif return assembly; } - - /// - /// 热重载相关(未完成,无法使用) - /// - /// - /// - private unsafe Assembly UpdateAssembly(Assembly oldAssembly) - { - GetAvailableCompilation(); - if (Domain!.Name != "Default") - { - Domain.SetAssemblyLoadBehavior(_domainConfiguration._dependencyLoadBehavior); - } - -#if DEBUG - Stopwatch stopwatch = new(); - stopwatch.Start(); -#endif - Stream dllStream = new MemoryStream(); - Stream pdbStream = new MemoryStream(); - Stream metaStream = new MemoryStream(); - Stream? xmlStream = null; - if (DllFilePath != string.Empty) - { - dllStream = File.Create(DllFilePath); - } - - if (CommentFilePath != string.Empty) - { - xmlStream = File.Create(CommentFilePath); - } - - var debugInfoFormat = _debugConfiguration._informationFormat; - debugInfoFormat ??= PdbHelpers.GetPlatformSpecificDebugInformationFormat(); - if (_compilation!.Options.OptimizationLevel == OptimizationLevel.Debug) - { - - if (debugInfoFormat != DebugInformationFormat.Embedded) - { - if (string.IsNullOrEmpty(PdbFilePath)) - { - var tempPdbOutputFolder = Path.Combine(GlobalOutputFolder, Domain.Name!); - PdbFilePath = Path.Combine(tempPdbOutputFolder, $"{AssemblyName}.pdb"); - if (!Directory.Exists(tempPdbOutputFolder)) - { - Directory.CreateDirectory(tempPdbOutputFolder); - } - } - if (File.Exists(PdbFilePath)) - { - var tempPdbOutputFolder = Path.Combine(GlobalOutputFolder, Domain.Name!); - PdbFilePath = Path.Combine(tempPdbOutputFolder, $"repeate.{AssemblyName}.{Guid.NewGuid():N}.pdb"); - if (!Directory.Exists(tempPdbOutputFolder)) - { - Directory.CreateDirectory(tempPdbOutputFolder); - } - } - pdbStream = File.Create(PdbFilePath); - } - else - { - PdbFilePath = null; - } - } - - var emitOption = new EmitOptions( - //runtimeMetadataVersion: Assembly.GetExecutingAssembly().ImageRuntimeVersion, - //instrumentationKinds: [InstrumentationKind.TestCoverage], - includePrivateMembers: _includePrivateMembers, - metadataOnly: _isReferenceAssembly, - pdbFilePath: PdbFilePath, - debugInformationFormat: debugInfoFormat!.Value, - pdbChecksumAlgorithm: debugInfoFormat != null ? default(HashAlgorithmName) : null - ); - - if (_emitOptionHandle != null) - { - while (!_emitOptionHandle.IsEmpty) - { - while (_emitOptionHandle.TryDequeue(out var func)) - { - emitOption = func(emitOption); - } - } - } - - var compileResult = _compilation.Emit( - dllStream, - pdbStream: pdbStream, - xmlDocumentationStream: xmlStream, - //options: emitOption, - metadataPEStream: metaStream - - ); - LogCompilationEvent?.Invoke(_compilation.GetNatashaLog()); - - - if (compileResult.Success) - { - var ilDelta = AsReadOnlySpan(dllStream); - var pdbDelta = AsReadOnlySpan(pdbStream); - var metadataDelta = AsReadOnlySpan(metaStream); - 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); - _exception = NatashaExceptionAnalyzer.GetCompileException(_compilation, compileResult.Diagnostics); - throw _exception; - } - - static ReadOnlySpan AsReadOnlySpan(Stream input) - { - input.Seek(0, SeekOrigin.Begin); - // 创建一个 MemoryStream 对象来保存 Stream 的数据 - using MemoryStream ms = new(); - input.CopyTo(ms); - - // 将 MemoryStream 的数据转换为 Span 对象 - return ms.GetBuffer().AsSpan(); - } - } } diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Emit.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Emit.cs new file mode 100644 index 00000000..9f603d45 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Emit.cs @@ -0,0 +1,31 @@ +using Microsoft.CodeAnalysis.Emit; +using System; +using System.Collections.Concurrent; +/// +/// 程序集编译构建器 - EMIT选项 +/// +public sealed partial class AssemblyCSharpBuilder +{ + private ConcurrentQueue>? _emitOptionHandle; + /// + /// 追加对 emitOption 的处理逻辑. + /// + /// 一次性配置,不可重用. + /// 多次调用会进入配置队列. + /// 调用 后清空队列. + /// 调用 后清空队列. + /// 调用 后清空队列. + /// + /// + /// + /// 注:该配置属于一次性配置,若重复使用该配置逻辑,请在这次编译后重新调用该方法. + /// + /// + /// 链式对象(调用方法的实例本身). + public AssemblyCSharpBuilder ConfigEmitOptions(Func handleAndReturnNewEmitOption) + { + _emitOptionHandle ??= new ConcurrentQueue>(); + _emitOptionHandle.Enqueue(handleAndReturnNewEmitOption); + return this; + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Event.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Event.cs new file mode 100644 index 00000000..8d0be47c --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Event.cs @@ -0,0 +1,36 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System; +using System.Collections.Immutable; +using System.Reflection; + +/// +/// 程序集编译构建器 - 事件 +/// +public sealed partial class AssemblyCSharpBuilder +{ + /// + /// 监听编译日志事件,默认不监听. + /// + /// + /// 注:该事件会被缓存,复用时无需重复添加方法. + /// + public event Action? LogCompilationEvent; + /// + /// 流编译成功之后触发的事件. + /// + /// + /// 此时已编译结束,程序集已经生成并加载. + /// + public event Action? CompileSucceedEvent; + + + /// + /// 流编译失败之后触发的事件. + /// + /// + /// 此时已经编译结束, 但是编译失败. + /// + public event Action>? CompileFailedEvent; +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Log.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Log.cs index a3ecdfa5..8149fa54 100644 --- a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Log.cs +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Log.cs @@ -2,13 +2,6 @@ public sealed partial class AssemblyCSharpBuilder { - /// - /// 监听编译日志事件,默认不监听. - /// - /// - /// 注:该事件会被缓存,复用时无需重复添加方法. - /// - public event Action? LogCompilationEvent; /// /// 和使用 += log =>{} 一样. /// diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Ouput.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Ouput.cs index 0803cbc6..94c28639 100644 --- a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Ouput.cs +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.Ouput.cs @@ -1,4 +1,5 @@ -using System; +using Natasha.CSharp.Compiler.Utils; +using System; using System.IO; /// @@ -27,7 +28,25 @@ static AssemblyCSharpBuilder() } } - + private bool _cleanOutput; + /// + /// 重复编译时,强制清除已存在的文件. + /// + /// + public AssemblyCSharpBuilder WithForceCleanOutput() + { + _cleanOutput = true; + return this; + } + /// + /// 重复编译时,不清除已存在的文件. 使用 old -> repeate.guid.oldname 进行替换.(不指定默认使用该方案) + /// + /// + public AssemblyCSharpBuilder WithoutForceCleanOutput() + { + _cleanOutput = false; + return this; + } /// /// 设置程序集名称. /// @@ -132,6 +151,23 @@ public AssemblyCSharpBuilder WithFileOutput(string? folder = null) CommentFilePath = Path.Combine(OutputFolder, $"{AssemblyName}.xml"); return this; } + + + private string FileHandle(string file) + { + if (File.Exists(DllFilePath)) + { + if (_cleanOutput) + { + File.Delete(DllFilePath); + } + else + { + return NatashaFileRepeateHelper.GetAvaliableFilePath(DllFilePath); + } + } + return file; + } #endregion } diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.References.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.References.cs new file mode 100644 index 00000000..fac913b1 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/AssemblyCSharpBuilder.References.cs @@ -0,0 +1,106 @@ +using Microsoft.CodeAnalysis; +using System; +using System.Collections.Generic; +/// +/// 程序集编译构建器 - 引用选项 +/// +public sealed partial class AssemblyCSharpBuilder +{ + private Func, IEnumerable>? _referencesFilter; + private CombineReferenceBehavior _combineReferenceBehavior = CombineReferenceBehavior.UseCurrent; + private readonly ReferenceConfiguration _referenceConfiguration = new(); + private readonly List _specifiedReferences; + + /// + /// 该方法允许共享域参与编译. + /// + /// + /// [共享域] 元数据 [参与] 编译. + /// + /// + /// [当前域] 元数据 [参与] 编译. + /// + /// + /// + /// + /// 注:若两个域不同,且存在相同名称元数据,默认优先使用主域的元数据. + /// + /// 配置同名元数据的解决策略 + /// 链式对象(调用方法的实例本身). + public AssemblyCSharpBuilder WithCombineReferences(Action? action = null) + { + action?.Invoke(_referenceConfiguration); + _combineReferenceBehavior = CombineReferenceBehavior.CombineDefault; + return this; + } + + + /// + /// 配置编译元数据的合并行为. + /// + /// + /// [共享域] 元数据 [不参与] 编译. + /// + /// + /// [当前域] 元数据 [参与] 编译. + /// + /// + /// + /// 链式对象(调用方法的实例本身). + public AssemblyCSharpBuilder WithCurrentReferences() + { + _combineReferenceBehavior = CombineReferenceBehavior.UseCurrent; + return this; + } + + /// + /// 使用外部指定的元数据引用进行编译. + /// + /// + /// [共享域] 元数据 [不参与] 编译. + /// + /// + /// [当前域] 元数据 [不参与] 编译. + /// + /// + /// + /// + /// 使用 ClearOutsideReferences 可以清除本次传递的元数据引用. + /// + /// + /// 链式对象(调用方法的实例本身). + public AssemblyCSharpBuilder WithSpecifiedReferences(IEnumerable metadataReferences) + { + lock (_specifiedReferences) + { + _specifiedReferences.AddRange(metadataReferences); + } + _combineReferenceBehavior = CombineReferenceBehavior.UseSpecified; + return this; + } + + /// + /// 清除由 WithSpecifiedReferences 方法传入的元数据引用. + /// + /// 链式对象(调用方法的实例本身). + public AssemblyCSharpBuilder ClearOutsideReferences() + { + lock (_specifiedReferences) + { + _specifiedReferences.Clear(); + } + return this; + } + + + /// + /// 配置元数据引用过滤策略. + /// + /// + /// 链式对象(调用方法的实例本身). + public AssemblyCSharpBuilder SetReferencesFilter(Func, IEnumerable>? referencesFilter) + { + _referencesFilter = referencesFilter; + return this; + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/SemanticAnalaysis/UsingAnalysistor.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/SemanticAnalaysis/UsingAnalysistor.cs index 5228ba0b..02007a94 100644 --- a/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/SemanticAnalaysis/UsingAnalysistor.cs +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/CompileUnit/SemanticAnalaysis/UsingAnalysistor.cs @@ -70,13 +70,13 @@ static UsingAnalysistor() var node = error.GetTypeSyntaxNode(root); if (node != null) { - NatashaDiagnosticsExtension.RemoveUsingAndNode(node, errorNodes); + node.RemoveUsingAndNode(errorNodes); } } //命名空间“namespace”中不存在类型或命名空间名“name”(是否缺少程序集引用?) else if (error.Id == "CS0234") { - error.RemoveUsingAndNodesFromStartName(root, errorNodes); + error.RemoveDefaultUsingAndNodesByStartName(root, errorNodes); } } diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Component/Compiler/Utils/RuntimeInnerHelper.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Component/Compiler/Utils/RuntimeInnerHelper.cs index ff27fcdd..5fc6d4b2 100644 --- a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Component/Compiler/Utils/RuntimeInnerHelper.cs +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Component/Compiler/Utils/RuntimeInnerHelper.cs @@ -11,7 +11,7 @@ internal unsafe static class RuntimeInnerHelper internal readonly static ApplyUpdateDelegate ApplyUpdate; static RuntimeInnerHelper() { - AssemblyCSharpBuilder builder = new AssemblyCSharpBuilder(); + AssemblyCSharpBuilder builder = new(); var assembly = builder .UseDefaultLoadContext() .UseSimpleMode() diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Extension/NatashaDiagnosticsExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Extension/NatashaDiagnosticsExtension.cs index 3752576b..93ac5297 100644 --- a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Extension/NatashaDiagnosticsExtension.cs +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Extension/NatashaDiagnosticsExtension.cs @@ -42,7 +42,7 @@ public static void RemoveUsingAndNode(this UsingDirectiveSyntax usingDirectiveSy } - public static void RemoveUsingAndNodesFromStartName(this Diagnostic diagnostic, CompilationUnitSyntax root, HashSet removeCollection) + public static void RemoveDefaultUsingAndNodesByStartName(this Diagnostic diagnostic, CompilationUnitSyntax root, HashSet removeCollection) { var usingNode = GetTypeSyntaxNode(diagnostic, root); if (usingNode!=null) diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Utils/NatashaFileRepeateHelper.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Utils/NatashaFileRepeateHelper.cs new file mode 100644 index 00000000..6858adaa --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Utils/NatashaFileRepeateHelper.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Natasha.CSharp.Compiler.Utils +{ + internal static class NatashaFileRepeateHelper + { + public static string GetAvaliableFilePath(string file) + { + var tempOutputFolder = Path.GetDirectoryName(file); + if (!Directory.Exists(tempOutputFolder)) + { + Directory.CreateDirectory(tempOutputFolder); + } + var tempFileName = Path.GetFileName(file); + return Path.Combine(tempOutputFolder, $"repeate.{Guid.NewGuid():N}.{tempFileName}"); + } + } +}