diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f729a341..595a794a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -18,6 +18,18 @@ updates: - dependency-name: "System.Reflection.MetadataLoadContext" - dependency-name: "Microsoft.Extensions.DependencyModel" + - package-ecosystem: "nuget" + directory: "src/Natasha.CSharp/Natasha.CSharp.Compiler" + schedule: + interval: "daily" + commit-message: + prefix: "[DEPENDENCY SRC]" + labels: + - "dependencies" + ignore: + - dependency-name: "System.Reflection.MetadataLoadContext" + - dependency-name: "Microsoft.Extensions.DependencyModel" + - package-ecosystem: "github-actions" directory: ".github" schedule: diff --git a/.github/project.yml b/.github/project.yml index 92edc089..106f40a8 100644 --- a/.github/project.yml +++ b/.github/project.yml @@ -134,6 +134,52 @@ src: - name: Microsoft.Extensions.DependencyModel versions: versions_type: + - using_output: + enable: true + ignores: + - System + - System.Runtime.CompilerServices + - System.Reflection + id: D7EDD106-B744-4E0C-9CCE-D88F29EBC983 + is_ignored: false + is_folded: false + relative_path: src/Natasha.CSharp/Natasha.CSharp.Compiler/Natasha.CSharp.Compiler.csproj + project_name: Natasha.CSharp.Compiler + package_name: DotNetCore.Natasha.CSharp.Compiler + project_folder: src/Natasha.CSharp/Natasha.CSharp.Compiler + labels: + dependency_config: + type: nuget + interval: daily + commit_prefix: '[DEPENDENCY SRC]' + special_time: + special_time_zone: + labels: + - name: dependencies + description: 有依赖需要升级 + color: 4E04B0 + ignore: + - name: System.Reflection.MetadataLoadContext + versions: + versions_type: + - name: Microsoft.Extensions.DependencyModel + versions: + versions_type: + - using_output: + enable: true + ignores: + - System + - System.Runtime.CompilerServices + - System.Reflection + id: 84A54AF4-0683-48D4-B9D2-465B851E1EF1 + is_ignored: false + is_folded: false + relative_path: src/Natasha.CSharp/Natasha.CSharp.Template/Natasha.CSharp.Template.csproj + project_name: Natasha.CSharp.Template + package_name: DotNetCore.Natasha.CSharp.Template + project_folder: src/Natasha.CSharp/Natasha.CSharp.Template + labels: + dependency_config: test: folded_projects: [] global_labels: diff --git a/Natasha.sln b/Natasha.sln index d81c55f6..be2aba79 100644 --- a/Natasha.sln +++ b/Natasha.sln @@ -193,6 +193,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scanner", "scanner", "{B7AA scanner.sh = scanner.sh EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Natasha.CSharp.Compiler", "src\Natasha.CSharp\Natasha.CSharp.Compiler\Natasha.CSharp.Compiler.csproj", "{D7EDD106-B744-4E0C-9CCE-D88F29EBC983}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Natasha.CSharp.Template", "src\Natasha.CSharp\Natasha.CSharp.Template\Natasha.CSharp.Template.csproj", "{84A54AF4-0683-48D4-B9D2-465B851E1EF1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -343,6 +347,14 @@ Global {199906B2-7FED-4DFF-8364-C5C66A787F02}.Debug|Any CPU.Build.0 = Debug|Any CPU {199906B2-7FED-4DFF-8364-C5C66A787F02}.Release|Any CPU.ActiveCfg = Release|Any CPU {199906B2-7FED-4DFF-8364-C5C66A787F02}.Release|Any CPU.Build.0 = Release|Any CPU + {D7EDD106-B744-4E0C-9CCE-D88F29EBC983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7EDD106-B744-4E0C-9CCE-D88F29EBC983}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7EDD106-B744-4E0C-9CCE-D88F29EBC983}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7EDD106-B744-4E0C-9CCE-D88F29EBC983}.Release|Any CPU.Build.0 = Release|Any CPU + {84A54AF4-0683-48D4-B9D2-465B851E1EF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {84A54AF4-0683-48D4-B9D2-465B851E1EF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {84A54AF4-0683-48D4-B9D2-465B851E1EF1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {84A54AF4-0683-48D4-B9D2-465B851E1EF1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -408,6 +420,8 @@ Global {CF373547-E278-4940-B5F8-DB548D76EC75} = {4EC5B87C-1343-4247-8A6A-A63C9BFEFEDD} {199906B2-7FED-4DFF-8364-C5C66A787F02} = {4EC5B87C-1343-4247-8A6A-A63C9BFEFEDD} {B7AA9686-44B7-4170-82B2-BF8E3B892887} = {DD0B729A-C1D5-41E1-AE1B-FE66F4BC651E} + {D7EDD106-B744-4E0C-9CCE-D88F29EBC983} = {8C9B862A-B569-460C-8B74-E74C6DF0CAB3} + {84A54AF4-0683-48D4-B9D2-465B851E1EF1} = {8C9B862A-B569-460C-8B74-E74C6DF0CAB3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3004E730-B231-40FA-B75C-58D7DDE17679} diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ca44eb5c..a4597d52 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -14,12 +14,12 @@ .NET Core Community NMSAzulx preview - + true 19404084.png LICENSE $([MSBuild]::NormalizeDirectory('$(SolutionDir)', 'resources'))natasha.snk true - 1701;1702;0168;NETSDK1138;IDE0060;xUnit2000;CS0067;CS8321;CS0649;CS8604;CA1822; + 1701;1702;0168;NETSDK1138;IDE0060;xUnit2000;CS0067;CS8321;CS0649;CS8604;CA1822;RS1014;CS1591; diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Compile.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Compile.cs new file mode 100644 index 00000000..dec37db3 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Compile.cs @@ -0,0 +1,250 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Emit; +using Natasha.CSharp.Component.Exception; +using Natasha.CSharp.Extension.Inner; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; + +#if MULTI +using System.IO; +using System.Reflection; +using Natasha.CSharp.Component.Domain; +/// +/// 程序集编译构建器 - 编译选项 +/// +public sealed partial class AssemblyCSharpBuilder +{ + + private PluginLoadBehavior _compileReferenceBehavior; + private PluginLoadBehavior _compileAssemblyBehavior; + private Func? _referencePickFunc; + private Func, IEnumerable>? _referencesFilter; + private bool _combineReferences; + + public AssemblyCSharpBuilder WithReferenceCombine() + { + _combineReferences = true; + return this; + } + public AssemblyCSharpBuilder WithoutReferenceCombine() + { + _combineReferences = false; + return this; + } + /// + /// 配置主域及当前域的加载行为, Default 使用主域引用, Custom 使用当前域引用 + /// + /// + /// + public AssemblyCSharpBuilder CompileWithReferenceLoadBehavior(PluginLoadBehavior loadBehavior) + { + _compileReferenceBehavior = loadBehavior; + return this; + } + /// + /// 配置当前域程序集的加载行为 + /// + /// + /// + public AssemblyCSharpBuilder CompileWithAssemblyLoadBehavior(PluginLoadBehavior loadBehavior) + { + _compileAssemblyBehavior = loadBehavior; + return this; + } + + public AssemblyCSharpBuilder CompileWithSameNameReferencesFilter(Func? useAssemblyNameFunc = null) + { + _referencePickFunc = useAssemblyNameFunc; + return this; + } + + + public AssemblyCSharpBuilder CompileWithReferencesFilter(Func, IEnumerable>? referencesFilter) + { + _referencesFilter = referencesFilter; + return this; + } + + /// + /// 流编译成功之后触发的事件 + /// + public event Action? CompileSucceedEvent; + + + + /// + /// 流编译失败之后触发的事件 + /// + public event Action>? CompileFailedEvent; + + + public CSharpCompilation GetAvailableCompilation(Func? initOptionsFunc = null) + { +#if DEBUG + Stopwatch stopwatch = new(); + stopwatch.Start(); +#endif + + //Mark : 26ms + if (_compileReferenceBehavior == PluginLoadBehavior.None) + { + _compilerOptions.SetSupersedeLowerVersions(true); + } + + var options = _compilerOptions.GetCompilationOptions(); + if (initOptionsFunc != null) + { + options = initOptionsFunc(options); + } + IEnumerable references; + if (_combineReferences) + { + references = Domain.GetReferences(_compileReferenceBehavior, _referencePickFunc); + } + else + { + references = Domain.References.GetReferences(); + } + + if (_referencesFilter != null) + { + references = _referencesFilter(references); + } + _compilation = CSharpCompilation.Create(AssemblyName, SyntaxTrees, references, options); + +#if DEBUG + stopwatch.RestartAndShowCategoreInfo("[Compiler]", "获取编译单元", 2); +#endif + + if (EnableSemanticHandler) + { + foreach (var item in _semanticAnalysistor) + { + _compilation = item(this, _compilation, _semanticCheckIgnoreAccessibility); + } + } + +#if DEBUG + stopwatch.StopAndShowCategoreInfo("[Semantic]", "语义处理", 2); +#endif + return _compilation; + } + + /// + /// 将 SyntaxTrees 中的语法树编译到程序集.如果不成功会抛出 NatashaException. + /// + /// + /// + /// + /// + /// //程序集的域加载行为, 该行为决定了编译后的程序集随着依赖加载到域中的处理结果. + /// //和加载插件原理相同. + /// builder.CompileWithAssemblyLoadBehavior(enum); + /// + /// //编译单元的引用加载行为, 遇到同名不同版本的引用该如何处理. + /// builder.CompileWithReferenceLoadBehavior(enum); + /// builder.CompileWithReferencesFilter(func); + /// + /// + /// + /// + public Assembly GetAssembly(Assembly? currentAssembly = null) + { + +#if DEBUG + Stopwatch stopwatch = new(); + stopwatch.Start(); +#endif + Stream dllStream; + Stream pdbStream; + Stream? xmlStream = null; + if (DllFilePath != string.Empty) + { + dllStream = File.Create(DllFilePath); + } + else + { + dllStream = new MemoryStream(); + } + + if (PdbFilePath != string.Empty) + { + pdbStream = File.Create(PdbFilePath); + } + else + { + pdbStream = new MemoryStream(); + } + + if (XmlFilePath != string.Empty) + { + xmlStream = File.Create(XmlFilePath); + } + + if (currentAssembly != null) + { + _compilation = + GetAvailableCompilation(opt=>opt + .WithModuleName(AssemblyName) + .WithOutputKind(OutputKind.NetModule)); + + } + else if (_compilation == null) + { + _compilation = GetAvailableCompilation(); + } + + + var compileResult = _compilation.Emit( + dllStream, + pdbStream: pdbStream, + xmlDocumentationStream: xmlStream, + options: new EmitOptions(pdbFilePath: PdbFilePath == string.Empty ? null : PdbFilePath, debugInformationFormat: DebugInformationFormat.PortablePdb)); + + + LogCompilationEvent?.Invoke(_compilation.GetNatashaLog()); + + Assembly? assembly = currentAssembly; + 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); + } + CompileSucceedEvent?.Invoke(_compilation, assembly!); + + } + dllStream.Dispose(); + pdbStream?.Dispose(); + xmlStream?.Dispose(); + +#if DEBUG + stopwatch.StopAndShowCategoreInfo("[ Emit ]", "编译时长", 2); +#endif + + if (!compileResult.Success) + { + CompileFailedEvent?.Invoke(_compilation, compileResult.Diagnostics); + throw NatashaExceptionAnalyzer.GetCompileException(_compilation, compileResult.Diagnostics); + } + + return assembly!; + } + +} +#endif + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Domain.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Domain.cs new file mode 100644 index 00000000..499de64f --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Domain.cs @@ -0,0 +1,54 @@ +#if MULTI +using System.Runtime.Loader; + +/// +/// 程序集编译构建器 - 域 +/// +public sealed partial class AssemblyCSharpBuilder +{ + private NatashaReferenceDomain? _domain; + + + /// + /// 编译单元所在域. + /// + /// + /// + /// + /// + /// //程序集默认加载行为, 遇到同名不同版本的依赖也加载. + /// domain.SetAssemblyLoadBehavior(); + /// + /// + /// + /// + public NatashaReferenceDomain Domain + { + get + { + if (_domain == null) + { + + if (AssemblyLoadContext.CurrentContextualReflectionContext != default) + { + _domain = (NatashaReferenceDomain)(AssemblyLoadContext.CurrentContextualReflectionContext); + } + else + { + _domain = NatashaReferenceDomain.DefaultDomain; + } + + } + return _domain; + } + set + { + if (value == default) + { + value = NatashaReferenceDomain.DefaultDomain; + } + _domain = value; + } + } +} +#endif diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Ouput.Multi.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Ouput.Multi.cs new file mode 100644 index 00000000..1a87c1c1 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Ouput.Multi.cs @@ -0,0 +1,37 @@ +#if MULTI +using System; +using System.IO; + +/// +/// 程序集编译构建器-输出 +/// +public sealed partial class AssemblyCSharpBuilder +{ + + public AssemblyCSharpBuilder UseNatashaFileOut(string? folder = null) + { + if (folder == null) + { + if (OutputFolder == GlobalOutputFolder) + { + OutputFolder = Path.Combine(GlobalOutputFolder, Domain.Name!); + } + } + else + { + OutputFolder = folder; + } + if (!Directory.Exists(OutputFolder)) + { + Directory.CreateDirectory(OutputFolder); + } + DllFilePath = Path.Combine(OutputFolder, $"{AssemblyName}.dll"); + PdbFilePath = Path.Combine(OutputFolder, $"{AssemblyName}.pdb"); + XmlFilePath = Path.Combine(OutputFolder, $"{AssemblyName}.xml"); + return this; + } + +} +#endif + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Syntax.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Syntax.cs new file mode 100644 index 00000000..e193b3ad --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.Syntax.cs @@ -0,0 +1,49 @@ +#if MULTI +using System.Text; +/// +/// 程序集编译构建器 - 语法树相关 +/// +public sealed partial class AssemblyCSharpBuilder +{ + /// + /// 注入代码并拼接using + /// + /// 脚本代码 + /// using 拼接行为 + /// + public AssemblyCSharpBuilder Add(string script, UsingLoadBehavior usingLoadBehavior) + { + switch (usingLoadBehavior) + { + case UsingLoadBehavior.WithDefault: + return AddScript(DefaultUsing.UsingScript + script); + case UsingLoadBehavior.WithCurrent: + if (Domain == NatashaReferenceDomain.DefaultDomain) + { + return AddScript(DefaultUsing.UsingScript + script); + } + return AddScript(Domain.UsingRecorder + script); + case UsingLoadBehavior.WithAll: + if (Domain == NatashaReferenceDomain.DefaultDomain) + { + return AddScript(DefaultUsing.UsingScript + script); + } + StringBuilder usingBuilder = new(); + foreach (var item in Domain.UsingRecorder._usings) + { + if (!DefaultUsing.HasElement(item)) + { + usingBuilder.AppendLine($"using {item};"); + } + } + return AddScript(DefaultUsing.UsingScript + usingBuilder + script); + default: + return AddScript(script); + } + } +} +#endif + + + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.cs new file mode 100644 index 00000000..95c1158e --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/CompileUnit/AssemblyCSharpBuilder.cs @@ -0,0 +1,43 @@ +#if MULTI +using Natasha.CSharp.Compiler.SemanticAnalaysis; +using System; +using System.Runtime.CompilerServices; + +/// +/// 程序集编译构建器 +/// 默认开启语义过滤 +/// 默认域内引用优先 +/// 默认GUID作为程序集名 +/// +[assembly: InternalsVisibleTo("NatashaFunctionUT, PublicKey=002400000480000094000000060200000024000052534131000400000100010069acb31dd0d9918441d6ed2b49cd67ae17d15fd6ded4ccd2f99b4a88df8cddacbf72d5897bb54f406b037688d99f482ff1c3088638b95364ef614f01c3f3f2a2a75889aa53286865463fb1803876056c8b98ec57f0b3cf2b1185de63d37041ba08f81ddba0dccf81efcdbdc912032e8d2b0efa21accc96206c386b574b9d9cb8")] +public sealed partial class AssemblyCSharpBuilder +{ + + public AssemblyCSharpBuilder():this(Guid.NewGuid().ToString("N")) + { + + } + public AssemblyCSharpBuilder(string assemblyName) + { + EnableSemanticHandler = true; + _semanticCheckIgnoreAccessibility = true; + _combineReferences = true; + _compileReferenceBehavior = PluginLoadBehavior.UseDefault; + _parsingBehavior = UsingLoadBehavior.None; + OutputFolder = GlobalOutputFolder; + _compilerOptions = new(); + _semanticAnalysistor = new() + { + UsingAnalysistor._usingSemanticDelegate + }; + SyntaxTrees = new(); + AssemblyName = assemblyName; + DllFilePath = string.Empty; + PdbFilePath = string.Empty; + XmlFilePath = string.Empty; + } + +} +#endif + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Component/Domain/DomainManagement.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Component/Domain/DomainManagement.cs new file mode 100644 index 00000000..150b043b --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Component/Domain/DomainManagement.cs @@ -0,0 +1,114 @@ +#if MULTI +using System; +using System.Collections.Concurrent; +using static System.Runtime.Loader.AssemblyLoadContext; + +public sealed class DomainManagement +{ + + public static readonly ConcurrentDictionary Cache; + + static DomainManagement() + { + Cache = new ConcurrentDictionary(); + } + + + public static NatashaReferenceDomain Random() + { + return Create("N" + Guid.NewGuid().ToString("N")); + } + + + public static NatashaReferenceDomain Create(string key) + { + if (Cache.ContainsKey(key)) + { + return (NatashaReferenceDomain)(Cache[key].Target!); + } + else + { + Clear(); + var domain = new NatashaReferenceDomain(key); + Add(key, domain); + return domain; + } + } + + + public static void Clear() + { + foreach (var item in Cache) + { + if (!item.Value.IsAlive) + { + Cache!.Remove(item.Key); + } + } + } + + + + public static void Add(string key, NatashaReferenceDomain domain) + { + if (Cache.ContainsKey(key)) + { + if (!Cache[key].IsAlive) + { + Cache[key] = new WeakReference(domain); + } + } + else + { + Cache[key] = new WeakReference(domain, trackResurrection: true); + } + } + + public static NatashaReferenceDomain CurrentDomain + { + get + { + return CurrentContextualReflectionContext == default ? + NatashaReferenceDomain.DefaultDomain : + (NatashaReferenceDomain)CurrentContextualReflectionContext; + } + } + + + public static WeakReference Remove(string key) + { + if (Cache.ContainsKey(key)) + { + var result = Cache!.Remove(key); + if (result != default) + { + ((NatashaReferenceDomain)(result.Target!)).Dispose(); + } + return result!; + } + + throw new System.Exception($"Can't find key : {key}!"); + } + + + public static bool IsDeleted(string key) + { + if (Cache.TryGetValue(key,out var value)) + { + return !value!.IsAlive; + } + return true; + } + + + public static NatashaReferenceDomain? Get(string key) + { + if (Cache.ContainsKey(key)) + { + return (NatashaReferenceDomain)Cache[key].Target!; + } + return null; + } + +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Component/Domain/NatashaReferenceCache.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Component/Domain/NatashaReferenceCache.cs new file mode 100644 index 00000000..a7bc26ab --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Component/Domain/NatashaReferenceCache.cs @@ -0,0 +1,147 @@ +#if MULTI +using Microsoft.CodeAnalysis; +using Natasha.CSharp.Component.Domain; +using Natasha.Domain.Extension; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Reflection.PortableExecutable; + +namespace Natasha.CSharp.Component +{ + //与元数据相关 + //数据值与程序集及内存相关 + public sealed class NatashaReferenceCache + { + /// + /// 存放内存流编译过来的程序集与引用 + /// + private readonly ConcurrentDictionary _referenceCache; + private readonly ConcurrentDictionary _referenceNameCache; + public NatashaReferenceCache() + { + _referenceCache = new(); + _referenceNameCache = new(); + } + + public int Count { get { return _referenceCache.Count; } } + public void AddReference(AssemblyName assemblyName, MetadataReference reference, PluginLoadBehavior loadReferenceBehavior) + { + + var name = assemblyName.GetUniqueName(); + if (loadReferenceBehavior != PluginLoadBehavior.None) + { + if (_referenceNameCache.TryGetValue(name, out var oldAssemblyName)) + { + if (assemblyName.CompareWithDefault(oldAssemblyName, loadReferenceBehavior) == AssemblyLoadVersionResult.UseCustomer) + { + _referenceCache!.Remove(oldAssemblyName); + } + else + { + return; + } + } + } + _referenceNameCache[name] = assemblyName; + _referenceCache[assemblyName] = reference; + + } + public void AddReference(AssemblyName assemblyName, Stream stream, PluginLoadBehavior loadReferenceBehavior = PluginLoadBehavior.None) + { + AddReference(assemblyName, MetadataReference.CreateFromStream(stream), loadReferenceBehavior); + } + public void AddReference(AssemblyName assemblyName, string path, PluginLoadBehavior loadReferenceBehavior = PluginLoadBehavior.None) + { + 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); + var name = assemblyName.Name; + if (name != null) + { + _referenceNameCache!.Remove(name); + } + } + public void RemoveReference(string name) + { + if (_referenceNameCache.TryGetValue(name, out var assemblyName)) + { + RemoveReference(assemblyName); + } + } + public void Clear() + { + _referenceCache.Clear(); + _referenceNameCache.Clear(); + } + public IEnumerable GetReferences() + { + return _referenceCache.Values; + } + internal HashSet CombineWithDefaultReferences(NatashaReferenceCache defaultCache, PluginLoadBehavior loadBehavior = PluginLoadBehavior.None, Func? useAssemblyNameFunc = null) + { + var sets = new HashSet(_referenceCache.Values); + var excludeNods = new HashSet(); + var defaultReferences = defaultCache._referenceCache; + var defaultNameReferences = defaultCache._referenceNameCache; ; + if (loadBehavior != PluginLoadBehavior.None || useAssemblyNameFunc != null) + { + foreach (var item in _referenceNameCache) + { + if (defaultNameReferences.TryGetValue(item.Key, out var defaultAssemblyName)) + { + AssemblyLoadVersionResult funcResult; + if (useAssemblyNameFunc != null) + { + funcResult = useAssemblyNameFunc(defaultAssemblyName, item.Value); + if (funcResult == AssemblyLoadVersionResult.PassToNextHandler) + { + funcResult = item.Value.CompareWithDefault(defaultAssemblyName, loadBehavior); + } + } + else + { + funcResult = item.Value.CompareWithDefault(defaultAssemblyName, loadBehavior); + } + + if (funcResult == AssemblyLoadVersionResult.UseDefault) + { + excludeNods.Add(_referenceCache[item.Value]); + } + else if (funcResult == AssemblyLoadVersionResult.UseCustomer) + { + excludeNods.Add(defaultReferences[defaultAssemblyName]); + } + + } + } + } + sets.UnionWith(defaultReferences.Values); + sets.ExceptWith(excludeNods); + return sets; + } + + private static MetadataReference CreateMetadataReference(string path) + { + using (var stream = File.OpenRead(path)) + { + var moduleMetadata = ModuleMetadata.CreateFromStream(stream, PEStreamOptions.PrefetchMetadata); + var assemblyMetadata = AssemblyMetadata.Create(moduleMetadata); + return assemblyMetadata.GetReference(filePath: path); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Component/Domain/NatashaReferenceDomain.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Component/Domain/NatashaReferenceDomain.cs new file mode 100644 index 00000000..70bdbb17 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Component/Domain/NatashaReferenceDomain.cs @@ -0,0 +1,110 @@ +#if MULTI +using Microsoft.CodeAnalysis; +using Natasha.CSharp.Component; +using Natasha.CSharp.Component.Domain; +using Natasha.CSharp.Using; +using System; +using System.Collections.Generic; +using System.Reflection; + +public sealed class NatashaReferenceDomain : NatashaDomain +{ + public static new readonly NatashaReferenceDomain DefaultDomain; + static NatashaReferenceDomain() + { + + DefaultDomain = new NatashaReferenceDomain(); + DomainManagement.Add("Default", DefaultDomain); + + } + + + public static void AddDefaultReferenceAndUsing(AssemblyName assemblyName, string path) + { + DefaultDomain.References.AddReference(assemblyName, path); + DefaultUsing.AddUsing(assemblyName); + } + + + public IEnumerable GetReferences(PluginLoadBehavior loadBehavior = PluginLoadBehavior.None, Func? useAssemblyNameFunc = null) + { + if (Name == DefaultDomain.Name) + { + return References.GetReferences(); + } + else + { + return References.CombineWithDefaultReferences(DefaultDomain.References, loadBehavior, useAssemblyNameFunc); + } + } + + /// + /// 引用 记录 + /// + public readonly NatashaReferenceCache References; + /// + /// Using 记录 + /// + public readonly NatashaUsingCache UsingRecorder; + private NatashaReferenceDomain() : base() + { + References = new(); + UsingRecorder = new(); + LoadAssemblyReferencsWithPath += NatashaReferenceDomain_LoadAssemblyReferencsWithPath; + LoadAssemblyReferenceWithStream += NatashaReferenceDomain_LoadAssemblyReferenceWithStream; + } + + + public NatashaReferenceDomain(string key) : base(key) + { + References = new(); + UsingRecorder = new(); + LoadAssemblyReferencsWithPath += NatashaReferenceDomain_LoadAssemblyReferencsWithPath; + LoadAssemblyReferenceWithStream += NatashaReferenceDomain_LoadAssemblyReferenceWithStream; + } + + + private void NatashaReferenceDomain_LoadAssemblyReferenceWithStream(Assembly assembly, System.IO.Stream stream) + { + + References.AddReference(assembly.GetName(), stream); + if (Name == "Default") + { + DefaultUsing.AddUsing(assembly); + } + else + { + DefaultUsing.AddUsing(assembly); + } + //UsingRecorder.Using(assembly); + + } + + + private void NatashaReferenceDomain_LoadAssemblyReferencsWithPath(Assembly assembly, string path) + { + References.AddReference(assembly.GetName(), path); + if (Name == "Default") + { + DefaultUsing.AddUsing(assembly); + } + else + { + UsingRecorder.Using(assembly); + } + } + + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + LoadAssemblyReferencsWithPath -= NatashaReferenceDomain_LoadAssemblyReferencsWithPath; + LoadAssemblyReferenceWithStream -= NatashaReferenceDomain_LoadAssemblyReferenceWithStream; + //References.Clear(); + } + } + +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/Inner/CSharpCompilationExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/Inner/CSharpCompilationExtension.cs new file mode 100644 index 00000000..92c29716 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/Inner/CSharpCompilationExtension.cs @@ -0,0 +1,126 @@ +#if MULTI +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Natasha.CSharp.Extension.Inner +{ + + internal static class CSharpCompilationExtension + { + internal static NatashaCompilationLog GetNatashaLog(this CSharpCompilation compilation) + { + NatashaCompilationLog natashaCompilation = new(); + natashaCompilation.AddCompilationInfo("AssemblyName", compilation.AssemblyName ?? string.Empty); + natashaCompilation.AddCompilationInfo("Language", compilation.Language); + natashaCompilation.AddCompilationInfo("LanguageVersion", compilation.LanguageVersion.ToString()); + natashaCompilation.AddCompilationInfo("SyntaxTreeCount", compilation.SyntaxTrees.Length.ToString()); + natashaCompilation.AddCompilationInfo("ReferencesCount", compilation.References.Count().ToString()); + var errors = compilation.GetDiagnostics(); + if (errors.Length > 0) + { + Dictionary> syntaxCache = new(); + foreach (var item in compilation.GetDiagnostics()) + { + if (item.Location.SourceTree != null) + { + var tree = item.Location.SourceTree; + if (!syntaxCache.ContainsKey(tree)) + { + syntaxCache[tree] = new List(); + } + syntaxCache[tree].Add(item); + } + } + natashaCompilation.HasError = true; + foreach (var item in syntaxCache) + { + var codeText = item.Key.ToString(); + StringBuilder errorMessage = new(); + foreach (var error in item.Value) + { + var span = error.Location.GetLineSpan(); + errorMessage.AppendLine($"第{span.StartLinePosition.Line + 1}行,第{span.StartLinePosition.Character}个字符: 内容【{GetErrorMessage(codeText, error.Location.GetLineSpan())}】 {error.GetMessage()}"); + } + natashaCompilation.AddMessage(item.Value.Count, AddLineNumber(codeText), errorMessage.ToString()); + } + } + else + { + natashaCompilation.HasError = false; + foreach (var item in compilation.SyntaxTrees) + { + natashaCompilation.AddMessage(0, AddLineNumber(item.ToString()), item.GetFirstOopName()); + } + } + return natashaCompilation; + } + private static string GetErrorMessage(string content, FileLinePositionSpan linePositionSpan) + { + + var start = linePositionSpan.StartLinePosition; + var end = linePositionSpan.EndLinePosition; + + + var arrayLines = content.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + var currentErrorLine = arrayLines[start.Line]; + + + if (start.Line == end.Line) + { + + if (start.Character == end.Character) + { + + return currentErrorLine.Trim(); + + } + else + { + + return currentErrorLine[start.Character..end.Character].Trim(); + + } + + } + else + { + + StringBuilder builder = new(); + builder.AppendLine(currentErrorLine[start.Character..]); + for (int i = start.Line; i < end.Line - 1; i += 1) + { + + builder.AppendLine(arrayLines[i]); + + } + currentErrorLine = arrayLines[end.Line]; + builder.AppendLine(currentErrorLine[..end.Character]); + return builder.ToString(); + + } + + } + private static string AddLineNumber(string code) + { + + StringBuilder builder = new(); + var arrayLines = code.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + for (int i = 0; i < arrayLines.Length; i += 1) + { + + builder.AppendLine($"{i + 1}\t{arrayLines[i]}"); + + } + return builder.ToString(); + + } + } + +} +#endif + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaAssemblyExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaAssemblyExtension.cs new file mode 100644 index 00000000..3685ed59 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaAssemblyExtension.cs @@ -0,0 +1,35 @@ +#if MULTI +using System.Reflection; +using System.Runtime.Loader; + + +public static class NatashaAssemblyDomainExtension +{ + + + public static NatashaReferenceDomain GetDomain(this Assembly assembly) + { + + var assemblyDomain = AssemblyLoadContext.GetLoadContext(assembly); + if (assemblyDomain == AssemblyLoadContext.Default) + { + return NatashaReferenceDomain.DefaultDomain!; + } + return (NatashaReferenceDomain)assemblyDomain!; + + } + + + public static void DisposeDomain(this Assembly assembly) + { + + var domain = GetDomain(assembly); + if (domain.Name != "Default") + { + domain.Dispose(); + } + + } + +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaDelegateExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaDelegateExtension.cs new file mode 100644 index 00000000..7f79043e --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaDelegateExtension.cs @@ -0,0 +1,25 @@ +#if MULTI +using System; + + +public static class NatashaDelegateExtension +{ + + public static NatashaReferenceDomain GetDomain(this Delegate @delegate) + { + + return @delegate.Method.Module.Assembly.GetDomain(); + + } + + + + public static void DisposeDomain(this Delegate @delegate) + { + + @delegate.Method.Module.Assembly.DisposeDomain(); + + } +} + +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaDomainExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaDomainExtension.cs new file mode 100644 index 00000000..ab7a4e42 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaDomainExtension.cs @@ -0,0 +1,24 @@ +#if MULTI +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using static System.Runtime.Loader.AssemblyLoadContext; + +public static class NatashaDomainExtension +{ + + + /// + /// 创建一个以该字符串命名的域并锁定 + /// + /// + /// + public static ContextualReflectionScope NatashaDomainScope(this string domain) + { + return DomainManagement.Create(domain).EnterContextualReflection(); + } + +} + +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaTypeDomainExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaTypeDomainExtension.cs new file mode 100644 index 00000000..623a8d0d --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/Extension/NatashaTypeDomainExtension.cs @@ -0,0 +1,23 @@ +#if MULTI +using System; +public static class NatashaTypeDomainExtension +{ + + public static NatashaReferenceDomain GetDomain(this Type type) + { + + return type.Assembly.GetDomain(); + + } + + + + public static void DisposeDomain(this Type type) + { + + type.Assembly.DisposeDomain(); + + } + +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/NatashaInitializer.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/NatashaInitializer.cs new file mode 100644 index 00000000..f11fcfb8 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/NatashaInitializer.cs @@ -0,0 +1,204 @@ +#if MULTI +using Microsoft.CodeAnalysis; +using Microsoft.Extensions.DependencyModel; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net.Http.Headers; +using System.Reflection; +using System.Reflection.Metadata; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +public static class NatashaInitializer +{ + private static readonly object _lock = new(); + + private static bool _isCompleted = false; + public static void Preheating(Func? excludeReferencesFunc = null + , bool useRuntimeUsing = false + , bool useRuntimeReference = false + ) + { + + if (!_isCompleted) + { + lock (_lock) + { + if (_isCompleted) + { + return; + } + + _isCompleted = true; + + excludeReferencesFunc ??= (_, _) => false; +#if DEBUG + Stopwatch stopwatch = new(); + stopwatch.Start(); +#endif + + DefaultUsing.SetDefaultUsingFilter(excludeReferencesFunc); + NatashaDomain.SetDefaultAssemblyFilter(excludeReferencesFunc); +#if DEBUG + stopwatch.RestartAndShowCategoreInfo("[ Domain ]", "默认信息初始化", 1); +#endif + IEnumerable? paths = null; + Queue parallelLoopResults = new Queue(); + + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + CacheRuntimeAssembly(assemblies); + + var namespaceCacheFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Natasha.Namespace.cache"); + + if (useRuntimeReference) + { + parallelLoopResults.Enqueue(InitReferenceFromRuntime(assemblies)); + } + else + { + paths = NatashaReferencePathsHelper.GetReferenceFiles(excludeReferencesFunc); + if (paths != null && paths.Any()) + { + parallelLoopResults.Enqueue(InitReferenceFromPath(paths)); + } + } + if (useRuntimeUsing) + { + parallelLoopResults.Enqueue(InitUsingFromRuntime(assemblies)); + } + else + { + paths ??= NatashaReferencePathsHelper.GetReferenceFiles(excludeReferencesFunc); + if (paths != null && paths.Any()) + { + //更新缓存 + parallelLoopResults.Enqueue(InitUsingFromPath(paths)); + } + else if(File.Exists(namespaceCacheFilePath)) + { + //从缓存文件中读取 + var namespaceCache = File.ReadAllText(namespaceCacheFilePath, Encoding.UTF8); + var namespaceText = namespaceCache.Split("+?", StringSplitOptions.RemoveEmptyEntries); + DefaultUsing.AddUsing(false, namespaceText); + } + } + + while (parallelLoopResults.Count>0) + { + var result = parallelLoopResults.Dequeue(); + while (!result.IsCompleted) + { + Thread.Sleep(100); + } + } + DefaultUsing.ReBuildUsingScript(); + if (!useRuntimeUsing && paths != null && paths.Any()) + { + var namespaceCache = new StringBuilder(); + foreach (var preNamespace in DefaultUsing._defaultNamesapce) + { + namespaceCache.Append($"{preNamespace}+?"); + } + File.WriteAllText(namespaceCacheFilePath, namespaceCache.ToString(), Encoding.UTF8); + } +#if DEBUG + stopwatch.RestartAndShowCategoreInfo("[Reference]", "引用初始化", 1); +#endif + + AssemblyCSharpBuilder cSharpBuilder = new(); + cSharpBuilder.ConfigCompilerOption(item => item.AddSupperess("CS8019").UseSuppressReportor(false)); + using (DomainManagement.Random().CreateScope()) + { + cSharpBuilder.EnableSemanticHandler = true; + cSharpBuilder.Add("public class A{}", UsingLoadBehavior.WithDefault); + //Mark : 22.0M (Memory:2023-02-27) + var assembly = cSharpBuilder.GetAssembly(); + } + cSharpBuilder.Domain.Dispose(); + +#if DEBUG + stopwatch.StopAndShowCategoreInfo("[FirstCompile]", "编译初始化", 1); +#endif + GC.Collect(); + } + } + + } + +#if DEBUG + private static readonly object _showLock = new object(); + public static void Show(Assembly assembly) + { + lock (_showLock) + { + Console.WriteLine("Asssembly : " + assembly.FullName); + Console.WriteLine("Attribute : " + string.Join(",", assembly.CustomAttributes.Select(item => item.AttributeType.Name))); + Console.WriteLine("ReferenceLength : " + assembly?.GetReferencedAssemblies().Length); + Console.WriteLine("ReferenceAsssembly : " + string.Join(",", assembly?.GetReferencedAssemblies().Select(asm => asm.Name))); + } + } +#endif + + + private unsafe 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); + } + }); + } + //* + private static ParallelLoopResult InitUsingFromRuntime(Assembly[] assemblies) + { + return Parallel.ForEach(assemblies, assembly => + { + DefaultUsing.AddUsingWithoutCheckingkAndInternalUsing(assembly, false); + }); + } + + private static ParallelLoopResult CacheRuntimeAssembly(Assembly[] assemblies) + { + return Parallel.ForEach(assemblies, assembly => + { + NatashaDomain.AddAssemblyToDefaultCache(assembly); + }); + } + ///* + private unsafe static ParallelLoopResult InitUsingFromPath(IEnumerable paths) + { + var resolver = new PathAssemblyResolver(paths); + using var mlc = new MetadataLoadContext(resolver); + return Parallel.ForEach(paths, (path) => + { + + Assembly assembly = mlc.LoadFromAssemblyPath(path); + DefaultUsing.AddUsingWithoutCheck(assembly, false); + + }); + } + private unsafe static ParallelLoopResult InitReferenceFromPath(IEnumerable paths) + { + var resolver = new PathAssemblyResolver(paths); + using var mlc = new MetadataLoadContext(resolver); + return Parallel.ForEach(paths, (path) => + { + Assembly assembly = mlc.LoadFromAssemblyPath(path); + NatashaReferenceDomain.DefaultDomain.References.AddReference(assembly.GetName(), path); + + }); + } + +} + +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/NatashaManagement.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/NatashaManagement.cs new file mode 100644 index 00000000..3c8e312d --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/MultiDomain/NatashaManagement.cs @@ -0,0 +1,67 @@ +# if MULTI +using System; + + +public static partial class NatashaManagement +{ + + /// + /// 获取系统域 + /// + /// + public static NatashaReferenceDomain GetDefaultDomain() + { + return NatashaReferenceDomain.DefaultDomain; + } + /// + /// 新建一个域 + /// + /// + /// + public static NatashaReferenceDomain CreateDomain(string domainName) + { + return DomainManagement.Create(domainName); + } + /// + /// 新建一个随机域 + /// + /// + public static NatashaReferenceDomain CreateRandomDomain() + { + return DomainManagement.Random(); + } + + /// + /// 增加元数据引用,编译需要元数据支持. + /// + /// + /// 加载行为,如果有相同类型的引用, 那么此枚举会比较新旧程序集版本 + /// + 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; + } + + /// + /// 移除元数据引用,编译需要元数据支持. + /// + /// + /// 加载行为,如果有相同类型的引用, 那么此枚举会比较新旧程序集版本 + /// + public static bool RemoveGlobalReference(Type type, PluginLoadBehavior loadBehavior = PluginLoadBehavior.None) + { + if (type.Assembly.IsDynamic || type.Assembly.GetName() == null) + { + return false; + } + NatashaReferenceDomain.DefaultDomain.References.RemoveReference(type.Assembly.GetName()); + return true; + } +} + +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Natasha.CSharp.Compiler.csproj b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Natasha.CSharp.Compiler.csproj new file mode 100644 index 00000000..e59ead60 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Natasha.CSharp.Compiler.csproj @@ -0,0 +1,46 @@ + + + + netstandard2.0;netcoreapp3.1;net5.0;net6.0;net7.0 + Natasha 的 C# 版编译器 + DotNetCore.Natasha.CSharp.Compiler + 升级到最新版. + Roslyn;IL;Script;Dynamic;Natasha;NMS;Compiler; + true + 5.2.2.1 + 5.2.2.1 + 5.2.2.1 + + + + MULTI; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.CompileOption.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.CompileOption.cs new file mode 100644 index 00000000..79da8383 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.CompileOption.cs @@ -0,0 +1,51 @@ +using Microsoft.CodeAnalysis.CSharp; +using Natasha.CSharp.Compiler; +using System; + +/// +/// 程序集编译构建器 - 编译选项 +/// +public sealed partial class AssemblyCSharpBuilder +{ + private readonly NatashaCSharpCompilerOptions _compilerOptions; + private CSharpCompilation? _compilation; + public CSharpCompilation? Compilation { get { return _compilation; } } + /// + /// 配置编译选项. 此方法传入的实例 instance.
+ ///
+ /// + /// + /// + /// + /// //默认配置 + /// + /// //关闭空引用支持 + /// opt=>opt.SetNullableCompile(NullableContextOptions.Disable) + /// + /// //不处理同名不同版本的引用 + /// .SetSupersedeLowerVersions(false) + /// + /// //输出方式为dll + /// .SetOutputKind(OutputKind.DynamicallyLinkedLibrary) + /// + /// //启用 Release 优化 + /// .CompileAsRelease() + /// + /// //支持 Unsafe 编译 + /// .SetUnsafeCompiler(true) + /// + /// //任何 CPU 平台 + /// .SetPlatform(Platform.AnyCpu); + /// + /// + /// + /// + public AssemblyCSharpBuilder ConfigCompilerOption(Action action) + { + action(_compilerOptions); + return this; + } +} + + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Log.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Log.cs new file mode 100644 index 00000000..6baf6501 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Log.cs @@ -0,0 +1,14 @@ +using System; + +public sealed partial class AssemblyCSharpBuilder +{ + + public event Action? LogCompilationEvent; + public AssemblyCSharpBuilder SetLogEvent(Action logAction) + { + LogCompilationEvent = logAction; + return this; + } + +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Ouput.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Ouput.cs new file mode 100644 index 00000000..450742aa --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Ouput.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; + +/// +/// 程序集编译构建器-输出 +/// +public sealed partial class AssemblyCSharpBuilder +{ + + #region 输出设置相关 + public string AssemblyName; + public string DllFilePath; + public string PdbFilePath; + public string XmlFilePath; + public string OutputFolder; + /// + /// 默认的输出文件夹 + /// + public static readonly string GlobalOutputFolder; + static AssemblyCSharpBuilder() + { + + GlobalOutputFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, "DynamicLibraryFolders"); + if (!Directory.Exists(GlobalOutputFolder)) + { + Directory.CreateDirectory(GlobalOutputFolder); + } + + } + public AssemblyCSharpBuilder SetAssemblyName(string asmName) + { + AssemblyName = asmName; + return this; + } + #endregion + +} + + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Semantic.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Semantic.cs new file mode 100644 index 00000000..a9a05b76 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Semantic.cs @@ -0,0 +1,77 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Natasha.CSharp.Compiler; +using Natasha.CSharp.Compiler.SemanticAnalaysis; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; + +/// +/// 程序集编译构建器 - 语义 +/// +public sealed partial class AssemblyCSharpBuilder +{ + + private readonly List> _semanticAnalysistor; + private bool _semanticCheckIgnoreAccessibility; + + + + /// + /// 在语义分析时检测 可访问性问题, 默认分析. 降低性能. + /// + /// + [Obsolete("Use WithAnalysisAccessibility", true)] + public AssemblyCSharpBuilder AnalysisIgnoreAccessibility() + { + _semanticCheckIgnoreAccessibility = false; + return this; + } + public AssemblyCSharpBuilder WithAnalysisAccessibility() + { + _semanticCheckIgnoreAccessibility = false; + return this; + } + + /// + /// 不在语义分析时检测 可访问性问题, 可提升性能. + /// + /// + [Obsolete("Use WithoutAnalysisAccessibility", true)] + public AssemblyCSharpBuilder NotAnalysisIgnoreAccessibility() + { + _semanticCheckIgnoreAccessibility = true; + return this; + } + public AssemblyCSharpBuilder WithoutAnalysisAccessibility() + { + _semanticCheckIgnoreAccessibility = true; + return this; + } + + public AssemblyCSharpBuilder AddSemanticAnalysistor(Func func) + { + _semanticAnalysistor.Add(func); + return this; + } + + public AssemblyCSharpBuilder RemoveSemanticAnalysistor(Func func) + { + _semanticAnalysistor.Remove(func); + return this; + } + + public bool EnableSemanticHandler; + + public AssemblyCSharpBuilder ClearInnerSemanticAnalysistor() + { + _semanticAnalysistor.Remove(UsingAnalysistor._usingSemanticDelegate); + return this; + } + + +} + + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Share.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Share.cs new file mode 100644 index 00000000..711fd904 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Share.cs @@ -0,0 +1,39 @@ +using System; + +public sealed partial class AssemblyCSharpBuilder +{ + /// + /// 清空编译信息, 下次编译重组 Compilation 和语法树. + /// + /// + public AssemblyCSharpBuilder ClearCompilationCache() + { + _compilation = null; + return this; + } + + /// + /// 清空所有记录,包括编译信息和脚本记录,以及程序集名称. + /// + /// + public AssemblyCSharpBuilder Clear() + { + _compilation = null; + SyntaxTrees.Clear(); + AssemblyName = string.Empty; + return this; + } + + /// + /// 自动使用 GUID 作为程序集名称. + /// + /// + public AssemblyCSharpBuilder WithRandomAssenblyName() + { + AssemblyName = Guid.NewGuid().ToString("N"); + return this; + } + + +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Syntax.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Syntax.cs new file mode 100644 index 00000000..d5a3e718 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/AssemblyCSharpBuilder.Syntax.cs @@ -0,0 +1,107 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Natasha.CSharp.Component.Exception; +using Natasha.CSharp.Syntax; +using System; +using System.Collections.Generic; + +/// +/// 程序集编译构建器 - 语法树相关 +/// +public sealed partial class AssemblyCSharpBuilder +{ + + public readonly List SyntaxTrees; + private CSharpParseOptions? _options; + private UsingLoadBehavior _parsingBehavior; + /// + /// 配置语法树选项 + /// + /// + /// + public AssemblyCSharpBuilder ConfigSyntaxOptions(CSharpParseOptions cSharpParseOptions) + { + _options = cSharpParseOptions; + return this; + } + /// + /// 配置语法树选项 + /// + /// + /// + public AssemblyCSharpBuilder ConfigSyntaxOptions(Func cSharpParseOptionsAction) + { + _options = cSharpParseOptionsAction(new CSharpParseOptions()); + return this; + } + + public AssemblyCSharpBuilder ConfigUsingOptions(UsingLoadBehavior usingLoadBehavior) + { + _parsingBehavior = usingLoadBehavior; + return this; + } + + /// + /// 注入脚本 + /// + /// 脚本代码 + /// + public AssemblyCSharpBuilder Add(string script) + { + return Add(script, _parsingBehavior); + } + + + /// + /// 添加脚本 + /// + /// + private AssemblyCSharpBuilder AddScript(string script) + { + var tree = NatashaCSharpSyntax.ParseTree(script, _options); + var exception = NatashaExceptionAnalyzer.GetSyntaxException(tree); + if (exception != null) + { + throw exception; + } + else + { + lock (SyntaxTrees) + { + SyntaxTrees.Add(tree); + } + } + return this; + } + /// + /// 添加语法树 + /// + /// + public AssemblyCSharpBuilder Add(SyntaxTree tree) + { + tree = NatashaCSharpSyntax.FormartTree(tree, _options); + var exception = NatashaExceptionAnalyzer.GetSyntaxException(tree); + if (exception != null) + { + throw exception; + } + else + { + lock (SyntaxTrees) + { + SyntaxTrees.Add(tree); + } + } + return this; + } + + public AssemblyCSharpBuilder ClearScript() + { + SyntaxTrees.Clear(); + return this; + } + +} + + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/SemanticAnalaysis/UsingAnalysistor.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/SemanticAnalaysis/UsingAnalysistor.cs new file mode 100644 index 00000000..120db0db --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/CompileUnit/SemanticAnalaysis/UsingAnalysistor.cs @@ -0,0 +1,93 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Natasha.CSharp.Extension.Inner; +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Natasha.CSharp.Compiler.SemanticAnalaysis +{ + internal static class UsingAnalysistor + { + internal static readonly Func _usingSemanticDelegate; + static UsingAnalysistor() + { + + _usingSemanticDelegate = (builder, compilation, ignoreAccessibility) => + { + + var trees = compilation.SyntaxTrees; + foreach (var tree in trees) + { +#if DEBUG + Stopwatch stopwatch = new(); + stopwatch.Start(); +#endif + CompilationUnitSyntax root = tree.GetCompilationUnitRoot(); + + SemanticModel semantiModel = compilation.GetSemanticModel(tree, ignoreAccessibility); +#if DEBUG + stopwatch.RestartAndShowCategoreInfo("[Semantic]", "语义节点获取", 3); +#endif + + var errors = semantiModel.GetDiagnostics(); +#if DEBUG + stopwatch.StopAndShowCategoreInfo("[Semantic]", "语义诊断获取", 3); + stopwatch.Restart(); +#endif + + if (errors.Length > 0) + { + var errorNodes = new HashSet(); + for (int i = 0; i < errors.Length; i++) + { + var error = errors[i]; + if (error.Id == "CS0434") + { + error.RemoveDefaultUsingAndUsingNode(root, errorNodes); + } + else if (error.Id == "CS8019") + { + var node = error.GetTypeSyntaxNode(root); + if (node != null) + { + errorNodes.Add(node); + } + + } + else if (error.Id == "CS0246") + { + var node = error.GetTypeSyntaxNode(root); + if (node != null) + { + NatashaDiagnosticsExtension.RemoveUsingAndNode(node, errorNodes); + } + } + else if (error.Id == "CS0234") + { + error.RemoveUsingAndNodesFromStartName(root, errorNodes); + } + } + +#if DEBUG + stopwatch.RestartAndShowCategoreInfo("[Semantic]", "语义节点筛查", 3); + +#endif + if (errorNodes.Count > 0) + { + compilation = compilation.ReplaceSyntaxTree(tree, root.RemoveNodes(errorNodes, SyntaxRemoveOptions.KeepNoTrivia)!.SyntaxTree); + } + +#if DEBUG + stopwatch.StopAndShowCategoreInfo("[Semantic]", "语义节点替换", 3); +#endif + } + + } + return compilation; + }; + } + + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/GlobalSupperessCache.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/GlobalSupperessCache.cs new file mode 100644 index 00000000..1fab6bbd --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/GlobalSupperessCache.cs @@ -0,0 +1,45 @@ +using Microsoft.CodeAnalysis; +using System.Collections.Concurrent; + +namespace Natasha.CSharp.Component.Compiler +{ + internal static class GlobalSupperessCache + { + internal static readonly ConcurrentDictionary _globalSuppressDiagnostics; + static GlobalSupperessCache() + { + _globalSuppressDiagnostics = new(); + AddGlobalSupperess("CA1050"); + AddGlobalSupperess("CA1822"); + AddGlobalSupperess("CS1701"); + AddGlobalSupperess("CS1702"); + AddGlobalSupperess("CS1705"); + AddGlobalSupperess("CS2008"); + AddGlobalSupperess("CS162"); + AddGlobalSupperess("CS0219"); + AddGlobalSupperess("CS0414"); + AddGlobalSupperess("CS0616"); + AddGlobalSupperess("CS0649"); + AddGlobalSupperess("CS0693"); + AddGlobalSupperess("CS1591"); + AddGlobalSupperess("CS1998"); + //AddGlobalSupperess("RS1014"); + //AddGlobalSupperess("CA1822"); + //AddGlobalSupperess("CS8604"); + // CS8019 + // CS0162 - Unreachable code detected. + // CS0219 - The variable 'V' is assigned but its value is never used. + // CS0414 - The private field 'F' is assigned but its value is never used. + // CS0616 - Member is obsolete. + // CS0649 - Field 'F' is never assigned to, and will always have its default value. + // CS0693 - Type parameter 'type parameter' has the same name as the type parameter from outer type 'T' + // CS1591 - Missing XML comment for publicly visible type or member 'Type_or_Member' + // CS1998 - This async method lacks 'await' operators and will run synchronously + + } + public static void AddGlobalSupperess(string errorcode) + { + _globalSuppressDiagnostics[errorcode] = ReportDiagnostic.Suppress; + } + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Model/CompilerBinderFlags.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Model/CompilerBinderFlags.cs new file mode 100644 index 00000000..26ba9290 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Model/CompilerBinderFlags.cs @@ -0,0 +1,111 @@ +using System; + +namespace Natasha.CSharp.Compiler +{ + + [Flags] + public enum CompilerBinderFlags : uint + { + None, // No specific location + SuppressConstraintChecks = 1 << 0, + SuppressObsoleteChecks = 1 << 1, + ConstructorInitializer = 1 << 2, + FieldInitializer = 1 << 3, + ObjectInitializerMember = 1 << 4, // object initializer field/property access + CollectionInitializerAddMethod = 1 << 5, // used for collection initializer add method overload resolution diagnostics + AttributeArgument = 1 << 6, + GenericConstraintsClause = 1 << 7, // "where" clause (used for cycle checking) + Cref = 1 << 8, // documentation comment cref + CrefParameterOrReturnType = 1 << 9, // Same as Cref, but lookup considers inherited members + + /// + /// Indicates that the current context allows unsafe constructs. + /// + /// + /// NOTE: Dev10 doesn't seem to treat attributes as being within the unsafe region. + /// Fortunately, not following this behavior should not be a breaking change since + /// attribute arguments have to be constants and there are no constants of unsafe + /// types. + /// + UnsafeRegion = 1 << 10, + + /// + /// Indicates that the unsafe diagnostics are not reported in the current context, regardless + /// of whether or not it is (part of) an unsafe region. + /// + SuppressUnsafeDiagnostics = 1 << 11, + + /// + /// Indicates that this binder is being used to answer SemanticModel questions (i.e. not + /// for batch compilation). + /// + /// + /// Imports touched by a binder with this flag set are not consider "used". + /// + SemanticModel = 1 << 12, + + EarlyAttributeBinding = 1 << 13, + + /// Remarks, mutually exclusive with . + CheckedRegion = 1 << 14, + /// Remarks, mutually exclusive with . + UncheckedRegion = 1 << 15, + + // Each of these produces a different diagnostic, so we need separate flags. + InLockBody = 1 << 16, // body, not the expression + InCatchBlock = 1 << 17, + InFinallyBlock = 1 << 18, + InTryBlockOfTryCatch = 1 << 19, // try block must have at least one catch clause + InCatchFilter = 1 << 20, + + // Indicates that this binder is inside of a finally block that is nested inside + // of a catch block. This flag resets at every catch clause in the binder chain. + // This flag is only used to support CS0724. Implies that InFinallyBlock and + // InCatchBlock are also set. + InNestedFinallyBlock = 1 << 21, + + IgnoreAccessibility = 1 << 22, + + ParameterDefaultValue = 1 << 23, + + /// + /// In the debugger, one can take the address of a managed object. + /// + AllowManagedAddressOf = 1 << 24, + + /// + /// In the debugger, the context is always unsafe, but one can still await. + /// + AllowAwaitInUnsafeContext = 1 << 25, + + /// + /// Ignore duplicate types from the cor library. + /// + IgnoreCorLibraryDuplicatedTypes = 1 << 26, + + /// + /// This is a , or has as its parent. + /// + InContextualAttributeBinder = 1 << 27, + + /// + /// Are we binding for the purpose of an Expression Evaluator + /// + InEEMethodBinder = 1 << 28, + + /// + /// Skip binding type arguments (we use instead). + /// For example, currently used when type constraints are bound in some scenarios. + /// + SuppressTypeArgumentBinding = 1 << 29, + + /// + /// The current context is an expression tree + /// + InExpressionTree = 1 << 30, + + + // Groups + AllClearedAtExecutableCodeBoundary = InLockBody | InCatchBlock | InCatchFilter | InFinallyBlock | InTryBlockOfTryCatch | InNestedFinallyBlock, + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Model/Core31Supplement.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Model/Core31Supplement.cs new file mode 100644 index 00000000..652d7fef --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Model/Core31Supplement.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace System.Runtime.CompilerServices +{ + +#if !NET5_0_OR_GREATER + public sealed class SkipLocalsInitAttribute : Attribute { } +#endif + +} \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Model/UsingLoadBehavior.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Model/UsingLoadBehavior.cs new file mode 100644 index 00000000..20bc36cf --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Model/UsingLoadBehavior.cs @@ -0,0 +1,8 @@ +public enum UsingLoadBehavior +{ + None = 0, + WithDefault = 1, + WithCurrent = 2, + WithAll = 3 +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/NatashaCSharpCompilerOptions.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/NatashaCSharpCompilerOptions.cs new file mode 100644 index 00000000..2868058d --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/NatashaCSharpCompilerOptions.cs @@ -0,0 +1,191 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Natasha.CSharp.Component.Compiler; +using Natasha.CSharp.Component.Compiler.Utils; +using System; +using System.Collections.Concurrent; + + +namespace Natasha.CSharp.Compiler +{ + public sealed class NatashaCSharpCompilerOptions + { + + public NatashaCSharpCompilerOptions() + { + _suppressDiagnostics = new ConcurrentDictionary(GlobalSupperessCache._globalSuppressDiagnostics); +#if DEBUG + this.SetCompilerFlag(CompilerBinderFlags.IgnoreCorLibraryDuplicatedTypes | CompilerBinderFlags.IgnoreAccessibility) +#else + this.SetCompilerFlag(CompilerBinderFlags.IgnoreCorLibraryDuplicatedTypes) +#endif + .SetNullableCompile(NullableContextOptions.Disable) + .SetSupersedeLowerVersions(false) + .SetOutputKind(OutputKind.DynamicallyLinkedLibrary) + .CompileAsRelease() + .SetUnsafeCompiler(true) + .SetPlatform(Platform.AnyCpu); + + } + + private readonly ConcurrentDictionary _suppressDiagnostics; + + public NatashaCSharpCompilerOptions AddSupperess(string errorcode) + { + _suppressDiagnostics[errorcode] = ReportDiagnostic.Suppress; + return this; + } + public NatashaCSharpCompilerOptions IgnoreWarning(string errorcode) + { + _suppressDiagnostics[errorcode] = ReportDiagnostic.Suppress; + return this; + } + public NatashaCSharpCompilerOptions RemoveSupperess(string errorcode) + { + _suppressDiagnostics.Remove(errorcode); + return this; + } + + + private bool _suppressReportShut; + public NatashaCSharpCompilerOptions UseSuppressReportor(bool shut) + { + _suppressReportShut = shut; + return this; + } + + + private NullableContextOptions _nullableCompileOption; + public NatashaCSharpCompilerOptions SetNullableCompile(NullableContextOptions flag) + { + _nullableCompileOption = flag; + return this; + } + + + private OptimizationLevel _codeOptimizationLevel; + public NatashaCSharpCompilerOptions CompileAsDebug() + { + _codeOptimizationLevel = OptimizationLevel.Debug; + return this; + } + public NatashaCSharpCompilerOptions CompileAsRelease() + { + _codeOptimizationLevel = OptimizationLevel.Release; + return this; + } + + + private Platform _processorPlatform; + public NatashaCSharpCompilerOptions SetPlatform(Platform flag) + { + _processorPlatform = flag; + return this; + } + + private OutputKind _assemblyKind; + public NatashaCSharpCompilerOptions SetOutputKind(OutputKind flag) + { + _assemblyKind = flag; + return this; + } + + private bool _allowUnsafe; + public NatashaCSharpCompilerOptions SetUnsafeCompiler(bool shut) + { + _allowUnsafe = shut; + return this; + } + + + private bool _referencesSupersedeLowerVersions; + /// + /// 自动禁用低版本程序集 + /// + /// + public NatashaCSharpCompilerOptions SetSupersedeLowerVersions(bool shut) + { + _referencesSupersedeLowerVersions = shut; + return this; + } + + + private CompilerBinderFlags _compileFlags; + /// + /// 绑定编译标识 + /// + /// + public NatashaCSharpCompilerOptions SetCompilerFlag(CompilerBinderFlags flags) + { + _compileFlags = flags; + return this; + } + + /// + /// 移除 IgnoreAccessibility 标识 + /// + /// + public NatashaCSharpCompilerOptions RemoveIgnoreAccessibility() + { + if (_compileFlags.HasFlag(CompilerBinderFlags.IgnoreAccessibility)) + { + _compileFlags = (uint)_compileFlags - CompilerBinderFlags.IgnoreAccessibility; + } + return this; + } + + /// + /// 获取构建编译信息的选项 + /// + /// + internal CSharpCompilationOptions GetCompilationOptions() + { + + var compilationOptions = new CSharpCompilationOptions( + nullableContextOptions: _nullableCompileOption, + //strongNameProvider: a, + deterministic: false, + concurrentBuild: true, + moduleName: Guid.NewGuid().ToString(), + reportSuppressedDiagnostics: _suppressReportShut, + metadataImportOptions: MetadataImportOptions.All, + outputKind: _assemblyKind, + optimizationLevel: _codeOptimizationLevel, + allowUnsafe: _allowUnsafe, + platform: _processorPlatform, + checkOverflow: false, + assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default, + specificDiagnosticOptions: _suppressDiagnostics); + if (_compileFlags != 0) + { + CompilerInnerHelper.SetTopLevelBinderFlagDelegate(compilationOptions, (uint)_compileFlags); + } + //CS1704 + CompilerInnerHelper.SetReferencesSupersedeLowerVersionsDelegate(compilationOptions, _referencesSupersedeLowerVersions); + return compilationOptions; + + } + + + ///// + ///// 获取编译选项 + ///// + ///// + ///// + //public CSharpCompilation GetCompilation() + //{ + // if (_compilation==default) + // { + // _compilation = CSharpCompilation.Create(AssemblyName, null, Domain.GetCompileReferences(), GetCompilationOptions()); + // } + // return _compilation.RemoveAllSyntaxTrees(); + + //} + + + } +} + + + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Utils/CompilerInnerHelper.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Utils/CompilerInnerHelper.cs new file mode 100644 index 00000000..ce1c2aa1 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Compiler/Utils/CompilerInnerHelper.cs @@ -0,0 +1,26 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System; +using System.Reflection; + +namespace Natasha.CSharp.Component.Compiler.Utils +{ + internal static class CompilerInnerHelper + { + internal static readonly Action SetTopLevelBinderFlagDelegate; + internal static readonly Action SetReferencesSupersedeLowerVersionsDelegate; + + static CompilerInnerHelper() + { + SetTopLevelBinderFlagDelegate = (Action)Delegate.CreateDelegate( + typeof(Action), typeof(CSharpCompilationOptions) + .GetProperty("TopLevelBinderFlags", BindingFlags.Instance | BindingFlags.NonPublic)! + .SetMethod!); + + SetReferencesSupersedeLowerVersionsDelegate = (Action)Delegate.CreateDelegate( + typeof(Action), typeof(CompilationOptions) + .GetProperty("ReferencesSupersedeLowerVersions", BindingFlags.Instance | BindingFlags.NonPublic)! + .SetMethod!); + } + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Exception/Model/ExceptionKind.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Exception/Model/ExceptionKind.cs new file mode 100644 index 00000000..66d4b7fa --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Exception/Model/ExceptionKind.cs @@ -0,0 +1,11 @@ +public enum NatashaExceptionKind +{ + None, + Assembly, + Type, + Method, + Delegate, + Syntax, + Compile +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Exception/Model/NatashaException.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Exception/Model/NatashaException.cs new file mode 100644 index 00000000..448ad0d0 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Exception/Model/NatashaException.cs @@ -0,0 +1,36 @@ +using Microsoft.CodeAnalysis; +using System; +using System.Collections.Generic; + + + +[Serializable] +public sealed class NatashaException : Exception +{ + + public NatashaException(string message) : base(message) + { + Formatter = string.Empty; + Diagnostics = new List(); + ErrorKind = NatashaExceptionKind.None; + CompileMessage = string.Empty; + } + + //格式化后的脚本字符串 + public string Formatter; + + //错误类型 + public NatashaExceptionKind ErrorKind; + + //roslyn诊断集合 + public List Diagnostics; + + /// + /// 详细的编译信息 + /// + public string CompileMessage; + +} + + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Exception/NatashaExceptionAnalyzer.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Exception/NatashaExceptionAnalyzer.cs new file mode 100644 index 00000000..05b69919 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Exception/NatashaExceptionAnalyzer.cs @@ -0,0 +1,43 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System.Collections.Immutable; +using System.Linq; + +namespace Natasha.CSharp.Component.Exception +{ + internal sealed class NatashaExceptionAnalyzer + { + + internal static NatashaException? GetSyntaxException(SyntaxTree tree) + { + + var diagnostics = tree.GetDiagnostics(); + var errors = diagnostics.Where(item => !item.IsSuppressed).ToArray(); + if (errors.Length>0) + { + var first = errors[0]; + var exception = new NatashaException(first.GetMessage()); + exception.Diagnostics.AddRange(errors); + exception.Formatter = tree.ToString(); + exception.ErrorKind = NatashaExceptionKind.Syntax; + return exception; + } + return null; + + } + + internal static NatashaException GetCompileException(CSharpCompilation compilation, ImmutableArray errors) + { + var first = errors[0]; + var exception = new NatashaException(first.GetMessage()); + exception.Diagnostics.AddRange(errors); + if (first.Location.SourceTree!=null) + { + exception.Formatter = first.Location.SourceTree.ToString(); + } + exception.CompileMessage = $"编译程序集为:{compilation.AssemblyName};CSharp版本:{compilation.LanguageVersion};"; + exception.ErrorKind = NatashaExceptionKind.Compile; + return exception; + } + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/NLog/NatashaCompilationLog.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/NLog/NatashaCompilationLog.cs new file mode 100644 index 00000000..7a14a084 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/NLog/NatashaCompilationLog.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Text; + +public sealed class NatashaCompilationLog +{ + public NatashaCompilationLog() + { + _csInfo = string.Empty; + Messages = new(); + CompilationInfomations = new(); + } + + public bool HasError; + + private string _csInfo; + + public string CompilationsSerializableInfomation + { + get + { + if (_csInfo == string.Empty) + { + _csInfo = $"AssemblyName:{CompilationInfomations["AssemblyName"]};Time:{DateTime.Now:yyyy-MM-dd HH:mm:ss};Language:{CompilationInfomations["Language"]};LanguageVersion:{CompilationInfomations["LanguageVersion"]};ReferencesCount:{CompilationInfomations["ReferencesCount"]}"; + } + return _csInfo; + } + } + + public readonly List Messages; + + public readonly Dictionary CompilationInfomations; + + internal void AddCompilationInfo(string key, string value) + { + CompilationInfomations[key] = value; + } + + internal void AddMessage(int count,string code,string message) + { + Messages.Add(new(count, code, message)); + } + + public override string ToString() + { + StringBuilder result = new StringBuilder(); + result.AppendLine($"{Environment.NewLine}============================== {(HasError?"ERROR":"SUCCEED")} : {CompilationInfomations["AssemblyName"]} =============================="); + if (HasError) + { + foreach (var item in Messages) + { + result.AppendLine($"{Environment.NewLine}------------------------------------------------------------------------------------------------------{Environment.NewLine}"); + result.AppendLine(item.Code); + result.AppendLine($"{Environment.NewLine}{item.Message}"); + } + + } + else + { + foreach (var item in Messages) + { + result.AppendLine($"{Environment.NewLine}------------------------------------------------- {item.Message} -------------------------------------------{Environment.NewLine}"); + result.AppendLine(item.Code); + } + } + + result.AppendLine($"{Environment.NewLine}------------------------------------------------------------------------------------------------------"); + result.AppendLine($"{Environment.NewLine} Time :\t{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); + result.AppendLine($"{Environment.NewLine} Language :\t{CompilationInfomations["Language"]} & {CompilationInfomations["LanguageVersion"]}"); + result.AppendLine($"{Environment.NewLine} TreeCount:\t共 {CompilationInfomations["SyntaxTreeCount"]} 个"); + result.AppendLine($"{Environment.NewLine} RefCount :\t共 {CompilationInfomations["ReferencesCount"]} 个"); + result.AppendLine($"{Environment.NewLine}------------------------------------------------------------------------------------------------------"); + result.AppendLine($"{Environment.NewLine}======================================================================================================"); + return result.ToString(); + } + + +} + +public class NatashaCompilationMessage +{ + private readonly int _count; + private readonly string _code; + private readonly string _message; + public NatashaCompilationMessage(int count, string code,string message) + { + _count = count; + _code = code; + _message = message; + } + public int ErrorCount { get { return _count; } } + public string Code { get { return _code; } } + public string Message { get { return _message; } } + +} + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Syntax/NatashaCSharpSyntax.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Syntax/NatashaCSharpSyntax.cs new file mode 100644 index 00000000..786e42f2 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Syntax/NatashaCSharpSyntax.cs @@ -0,0 +1,107 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System; + +namespace Natasha.CSharp.Syntax +{ + internal static class NatashaCSharpSyntax + { + + //private readonly static AdhocWorkspace _workSpace; + private readonly static CSharpParseOptions _options; + //private readonly static OptionSet _formartOptions; + static NatashaCSharpSyntax() + { + + _options = new CSharpParseOptions(LanguageVersion.Preview); + #region Settings + //var workspace = new AdhocWorkspace(); + //_formartOptions = workspace.Options; + ////_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.IndentBraces, true); + ////_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.IndentBlock, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.IndentSwitchCaseSection, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.IndentSwitchCaseSectionWhenBlock, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.IndentSwitchSection, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.LabelPositioning, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLineForCatch, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLineForClausesInQuery, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLineForElse, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLineForFinally, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLineForMembersInAnonymousTypes, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLineForMembersInObjectInit, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInAccessors, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInAnonymousMethods, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInAnonymousTypes, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInControlBlocks, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInLambdaExpressionBody, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInMethods, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInObjectCollectionArrayInitializers, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInProperties, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInTypes, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceAfterCast, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceAfterColonInBaseTypeDeclaration, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceAfterComma, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceAfterControlFlowStatementKeyword, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceAfterDot, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceAfterMethodCallName, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceAfterSemicolonsInForStatement, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceBeforeColonInBaseTypeDeclaration, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceBeforeComma, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceBeforeDot, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceBeforeOpenSquareBracket, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceBeforeSemicolonsInForStatement, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceBetweenEmptyMethodCallParentheses, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceBetweenEmptyMethodDeclarationParentheses, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceBetweenEmptySquareBrackets, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpacesIgnoreAroundVariableDeclaration, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceWithinCastParentheses, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceWithinExpressionParentheses, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceWithinMethodCallParentheses, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceWithinMethodDeclarationParenthesis, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceWithinOtherParentheses, false); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpaceWithinSquareBrackets, false); + ////_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpacingAfterMethodDeclarationName, true); + ////_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.SpacingAroundBinaryOperator, _options.Language); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.WrappingKeepStatementsOnSingleLine, true); + //_formartOptions = _formartOptions.WithChangedOption(CSharpFormattingOptions.WrappingPreserveSingleLine, true); + #endregion + + } + + + internal static SyntaxTree ParseTree(string script, CSharpParseOptions? options) + { + if (options==null) + { + options = _options; + } + //Mark1 : 647ms + //Mark2 : 128ms + //Mark : 5.0M (Memory:2023-02-27) + var tree = CSharpSyntaxTree.ParseText(script.Trim(), _options); + return FormartTree(tree, options); + + } + + + /// + /// 直接加载树,并缓存 + /// + /// + /// + internal static SyntaxTree FormartTree(SyntaxTree tree, CSharpParseOptions? options) + { + if (options == null) + { + options = _options; + } + //return tree.GetRoot().NormalizeWhitespace().SyntaxTree; + //Console.ReadKey(); + //Mark : 0.3M (Memory:2023-02-27) + //Roslyn BUG https://github.com/dotnet/roslyn/issues/58150 + return CSharpSyntaxTree.ParseText(tree.GetRoot().NormalizeWhitespace().SyntaxTree.ToString(), options); + } + } +} + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Using/DefaultUsing.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Using/DefaultUsing.cs new file mode 100644 index 00000000..ea5ecdd9 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Using/DefaultUsing.cs @@ -0,0 +1,444 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +/// +/// 全局 using, 初始化时会通过 DependencyModel 获取主域所需的引用 using 字符串. +/// +public static class DefaultUsing +{ + + internal readonly static HashSet _defaultNamesapce; + private static Func _excludeDefaultAssembliesFunc; + private static StringBuilder _usingScriptCache; + public static string UsingScript; + static DefaultUsing() + { + UsingScript = string.Empty; + _usingScriptCache = new StringBuilder(); + _defaultNamesapce = new HashSet(); + _excludeDefaultAssembliesFunc = (_, _) => false; + + } + + public static void SetDefaultUsingFilter(Func excludeDefaultAssembliesFunc) + { + _excludeDefaultAssembliesFunc = excludeDefaultAssembliesFunc; + } + + public static int Count { get { return _defaultNamesapce.Count; } } + + internal static void AddUsing(IEnumerable usings, bool autoRebuildScript = true) + { + try + { + lock (_defaultNamesapce) + { + foreach (var name in usings) + { + if (name.IndexOf('<') == -1) + { + _defaultNamesapce.Add(name); + _usingScriptCache.AppendLine($"using {name!};"); + } + } + if (autoRebuildScript) + { + UsingScript = _usingScriptCache.ToString(); + } + } + } + catch (Exception ex) + { +#if DEBUG + Console.WriteLine(ex.Message); +#endif + } + } + + internal static void AddUsingWithoutCheck(Assembly assembly, bool autoRebuildScript = true) + { + try + { + var tempSets = new HashSet(); + var types = assembly.ExportedTypes; + if (types.Count() > 16) + { + var result = Parallel.ForEach(types, type => + { + + if (type.IsNested && !type.IsNestedPublic) + { + return; + } + + var name = type.Namespace; + lock (tempSets) + { + if (tempSets.Contains(name)) + { + return; + } + } + if (!string.IsNullOrEmpty(name) + && name.IndexOf('<') == -1) + { + if (!_excludeDefaultAssembliesFunc(default!, name)) + { + lock (tempSets) + { + tempSets.Add(name); + } + } +#if DEBUG + else + { + System.Diagnostics.Debug.WriteLine("[排除程序集]:" + name); + } +#endif + + } + }); + while (!result.IsCompleted) + { + Thread.Sleep(100); + } + } + else + { + foreach (var type in types) + { + + if (type.IsNested && !type.IsNestedPublic) + { + continue; + } + + var name = type.Namespace; + + + if (!string.IsNullOrEmpty(name) + && !tempSets.Contains(name) + && name.IndexOf('<') == -1) + { + + if (!_excludeDefaultAssembliesFunc(default!, name)) + { + + tempSets.Add(name); + } +#if DEBUG + else + { + System.Diagnostics.Debug.WriteLine("[排除程序集]:" + name); + } +#endif + } + } + } + + //*/ + + lock (_defaultNamesapce) + { + foreach (var name in tempSets) + { + if (_defaultNamesapce.Add(name)) + { + _usingScriptCache.AppendLine($"using {name};"); + } + } + if (autoRebuildScript) + { + UsingScript = _usingScriptCache.ToString(); + } + } + + } + catch (Exception ex) + { +#if DEBUG + Console.WriteLine(assembly.FullName + ex.Message); +#endif + } + } + + internal static void AddUsingWithoutCheckingkAndInternalUsing(Assembly assembly, bool autoRebuildScript = true) + { + try + { + var tempSets = new HashSet(); + var types = assembly.ExportedTypes; + if (types.Count() > 16) + { + var result = Parallel.ForEach(types, type => + { + + if (type.IsNested && !type.IsNestedPublic) + { + return; + } + + var name = type.Namespace; + + lock (tempSets) + { + if (tempSets.Contains(name)) + { + return; + } + } + + if (!string.IsNullOrEmpty(name) + && !name.StartsWith("Internal") + && name.IndexOf('<') == -1) + { + + if (!_excludeDefaultAssembliesFunc(default!, name)) + { + lock (tempSets) + { + tempSets.Add(name); + } + } +#if DEBUG + else + { + System.Diagnostics.Debug.WriteLine("[排除程序集]:" + name); + } +#endif + } + }); + while (!result.IsCompleted) + { + Thread.Sleep(100); + } + } + else + { + foreach (var type in types) + { + + if (type.IsNested && !type.IsNestedPublic) + { + continue; + } + + var name = type.Namespace; + if (!string.IsNullOrEmpty(name) + && !name.StartsWith("Internal") + && !tempSets.Contains(name) + && name.IndexOf('<') == -1) + { + + if (!_excludeDefaultAssembliesFunc(default!, name)) + { + tempSets.Add(name); + } +#if DEBUG + else + { + System.Diagnostics.Debug.WriteLine("[排除程序集]:" + name); + } +#endif + } + } + } + + //*/ + + lock (_defaultNamesapce) + { + foreach (var name in tempSets) + { + if (_defaultNamesapce.Add(name)) + { + _usingScriptCache.AppendLine($"using {name};"); + } + } + if (autoRebuildScript) + { + UsingScript = _usingScriptCache.ToString(); + } + } + + } + catch (Exception ex) + { +#if DEBUG + Console.WriteLine(assembly.FullName + ex.Message); +#endif + } + } + + /// + /// 添加引用 + /// + /// + public static void AddUsing(Assembly assembly, bool autoRebuildScript = true) + { + try + { + var types = assembly.ExportedTypes; + lock (_defaultNamesapce) + { + foreach (var type in types) + { + if (type.IsNested && !type.IsNestedPublic) + { + continue; + } + + var name = type.Namespace; + if (!string.IsNullOrEmpty(name) + && name.IndexOf('<') == -1 + && !_defaultNamesapce.Contains(name) + ) + { + + if (!_excludeDefaultAssembliesFunc(default!, name)) + { + + _defaultNamesapce.Add(name); + _usingScriptCache.AppendLine($"using {name!};"); + + } +#if DEBUG + else + { + + System.Diagnostics.Debug.WriteLine("[排除程序集]:" + name); + + } +#endif + + } + } + if (autoRebuildScript) + { + UsingScript = _usingScriptCache.ToString(); + } + } + + } + catch (Exception ex) + { +#if DEBUG + Console.WriteLine(assembly.FullName + ex.Message); +#endif + } + } + + public static void ReBuildUsingScript() + { + UsingScript = _usingScriptCache.ToString(); + } + + /// + /// 添加引用 + /// + /// + internal static void AddUsing(AssemblyName assemblyName, bool autoRebuildScript = true) + { + try + { + lock (_defaultNamesapce) + { + var name = assemblyName.Name; + if (!string.IsNullOrEmpty(name) && name.IndexOf('<') == -1 && !_defaultNamesapce.Contains(name)) + { + _defaultNamesapce.Add(name); + _usingScriptCache.AppendLine($"using {name};"); + if (autoRebuildScript) + { + UsingScript = _usingScriptCache.ToString(); + } + } + + } + } + catch (Exception ex) + { +#if DEBUG + Console.WriteLine(assemblyName.FullName + ex.Message); +#endif + } + } + + public static void AddUsing(bool autoRebuildScript = true, params string[] namespaceText) + { + lock (_defaultNamesapce) + { + for (int i = 0; i < namespaceText.Length; i++) + { + var name = namespaceText[i]; + if (!string.IsNullOrEmpty(name) && name.IndexOf('<') == -1 && !_defaultNamesapce.Contains(name)) + { + _defaultNamesapce.Add(name); + _usingScriptCache.AppendLine($"using {name};"); + if (autoRebuildScript) + { + UsingScript = _usingScriptCache.ToString(); + } + } + } + + + } + } + + /// + /// 查询是否存在该命名空间 + /// + /// + /// + public static bool HasElement(string @namespace) + { + lock (_defaultNamesapce) + { + return _defaultNamesapce.Contains(@namespace); + } + } + + + /// + /// 移除命名空间 + /// + /// + public static void Remove(string @namespace, bool autoRebuildScript = true) + { + lock (_defaultNamesapce) + { + if (_defaultNamesapce.Contains(@namespace)) + { + _defaultNamesapce.Remove(@namespace); + _usingScriptCache = _usingScriptCache.Replace($"using {@namespace};{Environment.NewLine}", string.Empty); + if (autoRebuildScript) + { + UsingScript = _usingScriptCache.ToString(); + } + } + } + } + public static void Remove(IEnumerable namespaces, bool autoRebuildScript = true) + { + + lock (_defaultNamesapce) + { + _defaultNamesapce.ExceptWith(namespaces); + foreach (var item in namespaces) + { + _usingScriptCache = _usingScriptCache.Replace($"using {item};{Environment.NewLine}", string.Empty); + if (autoRebuildScript) + { + UsingScript = _usingScriptCache.ToString(); + } + } + } + + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Using/NatashaUsingCache.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Using/NatashaUsingCache.cs new file mode 100644 index 00000000..a476c5cf --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Component/Using/NatashaUsingCache.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Natasha.CSharp.Using +{ + /// + /// 引用模板 + /// + public sealed class NatashaUsingCache + { + + public readonly HashSet _usings; + //internal readonly HashSet _usingTypes; + //private static readonly Regex _using_regex; + + //static NatashaUsingCache() + //{ + // _using_regex = new Regex("[a-zA-Z.]+") + //} + public NatashaUsingCache() + { + + _usings = new HashSet(); + + } + + public int Count { get { return _usings.Count; } } + + + + public bool HasUsing(string @using) + { + return _usings.Contains(@using); + } + + + + public NatashaUsingCache Using(string? @using) + { + + if (!string.IsNullOrEmpty(@using) && @using!.IndexOf('<') == -1) + { + + _usings.Add(@using); + + } + return this; + + } + + public NatashaUsingCache Using(IEnumerable @using) + { + + _usings.UnionWith(@using); + return this; + + } + + + + + /// + /// 从程序集里获取引用 + /// + /// 程序集 + /// + public NatashaUsingCache Using(Assembly assembly) + { + + if (assembly != default) + { + try + { + Using(assembly.GetTypes()); + } + catch + { + + } + + } + return this; + + } + + + + /// + /// 设置命名空间 + /// + /// 命名空间 + /// + public NatashaUsingCache Using(params Assembly[] namespaces) + { + + for (int i = 0; i < namespaces.Length; i++) + { + + Using(namespaces[i]); + + } + return this; + + } + public NatashaUsingCache Using(IEnumerable namespaces) + { + + foreach (var item in namespaces) + { + Using(item); + } + return this; + + } + + + + + public NatashaUsingCache Using(IEnumerable namespaces) + { + + foreach (var item in namespaces) + { + + Using(item); + + } + return this; + + } + + + + + public NatashaUsingCache Using(Type type) + { + + return Using(type.Namespace); + + } + + + public override string ToString() + { + StringBuilder usings = new(); + foreach (var item in _usings) + { + usings.AppendLine($"using {item};"); + } + return usings.ToString(); + } + + } + +} \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/Inner/ConcurrentDictionaryExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/Inner/ConcurrentDictionaryExtension.cs new file mode 100644 index 00000000..3b0f92c6 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/Inner/ConcurrentDictionaryExtension.cs @@ -0,0 +1,18 @@ +using System.Collections.Concurrent; + +static class ConcurrentDictionaryExtension +{ + public static S? Remove(this ConcurrentDictionary dict, T key) where T : notnull where S : notnull + { + + while (!dict.TryRemove(key, out var result)) + { + if (!dict.ContainsKey(key)) + { + return result; + } + } + return default; + + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/Inner/StopwatchExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/Inner/StopwatchExtension.cs new file mode 100644 index 00000000..b84e1c1e --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/Inner/StopwatchExtension.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Concurrent; +using System.Diagnostics; + + +internal static class StopwatchExtension +{ + private static readonly ConcurrentDictionary _colorCache; + static StopwatchExtension() + { + _colorCache = new ConcurrentDictionary(); + _colorCache[new ScoreRange(0, 20)] = ConsoleColor.Green; + _colorCache[new ScoreRange(20, 100)] = ConsoleColor.Cyan; + _colorCache[new ScoreRange(100, 500)] = ConsoleColor.Yellow; + _colorCache[new ScoreRange(500, 1000)] = ConsoleColor.Magenta; + _colorCache[new ScoreRange(1000, 100000)] = ConsoleColor.Red; + } + internal static void StopAndShowCategoreInfo(this Stopwatch stopwatch, string nodeName, string info, int level) + { + stopwatch.Stop(); + ShowCategoreInfo(stopwatch, nodeName, info, level); + } + + + internal static void RestartAndShowCategoreInfo(this Stopwatch stopwatch, string nodeName, string info, int level) + { + stopwatch.Stop(); + ShowCategoreInfo(stopwatch, nodeName, info, level); + stopwatch.Restart(); + } + + + + internal static void ShowCategoreInfo(Stopwatch stopwatch, string nodeName, string info, int level) + { + var color = Console.ForegroundColor; + foreach (var item in _colorCache) + { + if (item.Key.IsInRange(stopwatch.ElapsedMilliseconds)) + { + Console.ForegroundColor = item.Value; + for (int i = 0; i < level; i += 1) + { + Console.Write("\t"); + } + Console.WriteLine($"---{nodeName}\t{info} : {stopwatch.ElapsedMilliseconds}ms"); + } + } + Console.ForegroundColor = color; + } + + + + /// + /// 设置颜色等级 + /// + /// + /// + /// + /// + internal static Stopwatch SetLevel(this Stopwatch stopwatch, ScoreRange scoreRange, ConsoleColor color) + { + _colorCache[scoreRange] = color; + return stopwatch; + } +} + +/// +/// 分数模型 +/// +internal class ScoreRange +{ + private readonly long _min; + private readonly long _max; + + internal ScoreRange(long min, long max) + { + _min = min; + _max = max; + } + /// + /// 判断得分是否在范围内 + /// + /// + /// + internal bool IsInRange(long score) + { + return _min <= score && score <= _max; + } +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/Inner/SyntaxNodeExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/Inner/SyntaxNodeExtension.cs new file mode 100644 index 00000000..8162af36 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/Inner/SyntaxNodeExtension.cs @@ -0,0 +1,194 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; +using System.Linq; + +namespace Natasha.CSharp.Extension.Inner +{ + internal static class SyntaxNodeExtension + { + + //internal static CompilationUnitSyntax GetRoot(string content) + //{ + + // SyntaxTree tree = CSharpSyntaxTree.ParseText(content, new CSharpParseOptions(LanguageVersion.Latest)); + // return tree.GetCompilationUnitRoot(); + + //} + + private static IEnumerable GetNodes(SyntaxNode node) + { + return node.DescendantNodes().OfType(); + } + + + + + /// + /// 根据命名空间和类的位置获取类型 + /// + /// 命名空间节点 + /// 命名空间里的第index-1个 类 + /// + internal static string? GetClassName(this SyntaxNode namespaceNode, int index = 0) + { + + return GetDataStructString(namespaceNode, index); + + } + + + + + /// + /// 根据命名空间和结构体的位置获取类型 + /// + /// 命名空间节点 + /// 命名空间里的第index-1个 结构体 + /// + internal static string? GetStructName(this SyntaxNode namespaceNode, int index = 0) + { + + return GetDataStructString(namespaceNode, index); + + } + + + /// + /// 根据命名空间和记录的位置获取类型 + /// + /// 命名空间节点 + /// 命名空间里的第index-1个 Record + /// + internal static string? GetRecordName(this SyntaxNode namespaceNode, int index = 0) + { + + return GetDataStructString(namespaceNode, index); + + } + + + + + /// + /// 根据命名空间和接口的位置获取类型 + /// + /// 命名空间节点 + /// 命名空间里的第index-1个接口 + /// + internal static string GetInterfaceName(this SyntaxNode namespaceNode, int index = 0) + { + + return GetDataStructString(namespaceNode, index); + + } + + + + + /// + /// 根据命名空间和枚举的位置获取类型 + /// + /// 命名空间节点 + /// 命名空间里的第index-1个枚举 + /// + internal static string GetEnumName(this SyntaxNode namespaceNode, int index = 0) + { + + return GetDataStructString(namespaceNode, index); + + } + + + + + + /// + /// 获取命名空间 + /// + /// + /// + /// + internal static SyntaxNode NamespaceNode(this SyntaxTree tree, int namespaceIndex = 0) + { + var root = tree.GetCompilationUnitRoot(); + var namespaceDeclarationSyntaxes = GetNodes(root); + + if (namespaceDeclarationSyntaxes.Any()) + { + return namespaceDeclarationSyntaxes.ElementAt(namespaceIndex); + } + else + { + return root; + } + } + + + + private static string GetDataStructString(SyntaxNode namespaceNode, int index = 0) where T : BaseTypeDeclarationSyntax + { + + var nodes = GetNodes(namespaceNode); + var node = nodes.ElementAtOrDefault(index); + if (node != null) + { + return node.Identifier.Text; + } + return string.Empty; + + } + + + + /// + /// 根据命名空间和方法的位置获取类型 + /// + /// 命名空间节点 + /// 命名空间里的第index-1个方法 + internal static string GetMethodName(this SyntaxNode namespaceNode, int index = 0) + { + + var nodes = GetNodes(namespaceNode); + var node = nodes.ElementAtOrDefault(index); + if (node != null) + { + return node.Identifier.Text; + } + return string.Empty; + + } + + + internal static string GetFirstOopName(this SyntaxTree syntaxTree) + { + var node = syntaxTree.NamespaceNode(); + var result = node.GetClassName(); + if (string.IsNullOrEmpty(result)) + { + result = node.GetStructName(); + if (string.IsNullOrEmpty(result)) + { + result = node.GetRecordName(); + if (string.IsNullOrEmpty(result)) + { + result = node.GetInterfaceName(); + if (string.IsNullOrEmpty(result)) + { + result = node.GetEnumName(); + if (string.IsNullOrEmpty(result)) + { + result = "Not found oop name!"; + } + } + } + } + } + return result!; + } + + + } +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaAssemblyBuilderExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaAssemblyBuilderExtension.cs new file mode 100644 index 00000000..ceff6bde --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaAssemblyBuilderExtension.cs @@ -0,0 +1,50 @@ +using Microsoft.CodeAnalysis; + + +public static class NatashaAssemblyBuilderExtension +{ + + public static AssemblyCSharpBuilder EnableNullableCompile(this AssemblyCSharpBuilder builder) + { + builder.ConfigCompilerOption(opt => opt.SetNullableCompile(NullableContextOptions.Enable)); + return builder; + } + public static AssemblyCSharpBuilder DisableNullableCompile(this AssemblyCSharpBuilder builder) + { + builder.ConfigCompilerOption(opt => opt.SetNullableCompile(NullableContextOptions.Disable)); + return builder; + } + + public static AssemblyCSharpBuilder SetOutputFolder(this AssemblyCSharpBuilder builder, string folder) + { + builder.OutputFolder = folder; + return builder; + } + public static AssemblyCSharpBuilder SetDllFilePath(this AssemblyCSharpBuilder builder, string dllFilePath) + { + builder.DllFilePath = dllFilePath; + return builder; + } + public static AssemblyCSharpBuilder SetPdbFilePath(this AssemblyCSharpBuilder builder, string pdbFilePath) + { + builder.PdbFilePath = pdbFilePath; + return builder; + } + public static AssemblyCSharpBuilder SetXmlFilePath(this AssemblyCSharpBuilder builder, string xmlFilePath) + { + builder.XmlFilePath = xmlFilePath; + return builder; + } + + public static AssemblyCSharpBuilder DisableSemanticCheck(this AssemblyCSharpBuilder builder) + { + builder.EnableSemanticHandler = false; + return builder; + } + public static AssemblyCSharpBuilder EnableSemanticCheck(this AssemblyCSharpBuilder builder) + { + builder.EnableSemanticHandler = true; + return builder; + } +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaAssemblyExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaAssemblyExtension.cs new file mode 100644 index 00000000..f5507795 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaAssemblyExtension.cs @@ -0,0 +1,188 @@ +using System; +using System.Linq; +using System.Reflection; + + +public static class NatashaAssemblyExtension +{ + + /// + /// 为统一 Exception 报错, 为 Assembly 封装扩展方法, 反射出类型. + /// + /// 要反射的程序集 + /// 反射的短类名 + /// + public static Type GetTypeFromShortName(this Assembly assembly, string typeName) + { + try + { + return assembly.GetTypes().First(item => item.Name == typeName); + } + catch (Exception ex) + { + throw new NatashaException($"无法在程序集 {assembly.FullName} 中找到该类型 {typeName}!错误信息:{ex.Message}") + { + ErrorKind = NatashaExceptionKind.Type + }; + } + + } + /// + /// 为统一 Exception 报错, 为 Assembly 封装扩展方法, 反射出类中的方法. + /// + /// 要反射的程序集 + /// 反射的短类名 + /// 类中的方法名 + /// + public static MethodInfo GetMethodFromShortName(this Assembly assembly, string typeName, string methodName) + { + + var type = GetTypeFromShortName(assembly, typeName); + try + { + var info = type.GetMethod(methodName); + if (info == null) + { + throw new Exception("获取方法返回空!"); + } + return info!; + } + catch (Exception ex) + { + + throw new NatashaException($"无法在程序集 {assembly.FullName} 中找到类型 {typeName} 对应的 {methodName} 方法!错误信息:{ex.Message}") + { + ErrorKind = NatashaExceptionKind.Method + }; + } + + } + /// + /// 为统一 Exception 报错, 为 Assembly 封装扩展方法, 反射出类中的方法委托. + /// + /// 要反射的程序集 + /// 反射的短类名 + /// 类中的方法名 + /// 委托类型 + /// 绑定信息 + /// + public static Delegate GetDelegateFromShortName(this Assembly assembly, string typeName, string methodName, Type delegateType, object? target = null) + { + + var info = GetMethodFromShortName(assembly, typeName, methodName); + + try + { + + return info.CreateDelegate(delegateType, target); + + } + catch (Exception ex) + { + + throw new NatashaException($"无法将程序集 {assembly.FullName} 类型为 {typeName} 的 {methodName} 方法转成委托 {delegateType.Name}!错误信息:{ex.Message}") + { + ErrorKind = NatashaExceptionKind.Delegate + }; + + } + + } + public static T GetDelegateFromShortName(this Assembly assembly, string typeName, string methodName, object? target = null) where T : Delegate + { + return (T)GetDelegateFromShortName(assembly, typeName, methodName, typeof(T), target); + } + + /// + /// 为统一 Exception 报错, 为 Assembly 封装扩展方法, 反射出类型. + /// + /// 要反射的程序集 + /// 反射的完整类名 + /// + public static Type GetTypeFromFullName(this Assembly assembly, string typeName) + { + + try + { + return assembly.GetTypes().First(item => item.GetDevelopName() == typeName); + } + catch (Exception ex) + { + throw new NatashaException($"无法在程序集 {assembly.FullName} 中找到该类型 {typeName}!错误信息:{ex.Message}") + { + ErrorKind = NatashaExceptionKind.Type + }; + } + + } + + /// + /// 为统一 Exception 报错, 为 Assembly 封装扩展方法, 反射出类中的方法. + /// + /// 要反射的程序集 + /// 反射的完整类名 + /// 类中的方法名 + /// + public static MethodInfo GetMethodFromFullName(this Assembly assembly, string typeName, string methodName) + { + + var type = GetTypeFromFullName(assembly, typeName); + try + { + var info = type.GetMethod(methodName); + if (info == null) + { + throw new Exception("获取方法返回空!"); + } + return info!; + } + catch (Exception ex) + { + + throw new NatashaException($"无法在程序集 {assembly.FullName} 中找到类型 {typeName} 对应的 {methodName} 方法!错误信息:{ex.Message}") + { + ErrorKind = NatashaExceptionKind.Method + }; + + } + + } + + /// + /// 为统一 Exception 报错, 为 Assembly 封装扩展方法, 反射出类中的方法委托. + /// + /// 要反射的程序集 + /// 反射的完整类名 + /// 类中的方法名 + /// 委托类型 + /// 绑定信息 + /// + public static Delegate GetDelegateFromFullName(this Assembly assembly, string typeName, string methodName, Type delegateType, object? target = null) + { + + var info = GetMethodFromFullName(assembly, typeName, methodName); + + try + { + + return info.CreateDelegate(delegateType, target); + + } + catch (Exception ex) + { + + throw new NatashaException($"无法将程序集 {assembly.FullName} 类型为 {typeName} 的 {methodName} 方法转成委托 {delegateType.Name}!错误信息:{ex.Message}") + { + ErrorKind = NatashaExceptionKind.Delegate + }; + + } + + } + public static T GetDelegateFromFullName(this Assembly assembly, string typeName, string methodName, object? target = null) where T : Delegate + { + return (T)GetDelegateFromFullName(assembly, typeName, methodName, typeof(T), target); + } + +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaDiagnosticsExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaDiagnosticsExtension.cs new file mode 100644 index 00000000..91e87e8f --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaDiagnosticsExtension.cs @@ -0,0 +1,59 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Collections.Generic; +using System.Linq; + +namespace Natasha.CSharp +{ + public static class NatashaDiagnosticsExtension + { + public static SyntaxNode GetSyntaxNode(this Diagnostic diagnostic, CompilationUnitSyntax root) + { + return root.FindNode(diagnostic.Location.SourceSpan); + } + public static T? GetTypeSyntaxNode(this Diagnostic diagnostic, CompilationUnitSyntax root) where T : class + { + var node = GetSyntaxNode(diagnostic, root); + while (node is not T && node.Parent != null) + { + node = node!.Parent; + } + return node as T; + } + public static void RemoveDefaultUsingAndUsingNode(this Diagnostic diagnostic, CompilationUnitSyntax root, HashSet removeCollection) + { + var usingNode = GetTypeSyntaxNode(diagnostic, root); + if (usingNode != null) + { + RemoveUsingAndNode(usingNode, removeCollection); + } + } + + public static void RemoveUsingAndNode(this UsingDirectiveSyntax usingDirectiveSyntax, HashSet removeCollection) + { + removeCollection.Add(usingDirectiveSyntax); + var name = usingDirectiveSyntax.Name; + if (name!=null) + { + DefaultUsing.Remove(name.ToString()); + } + + } + + public static void RemoveUsingAndNodesFromStartName(this Diagnostic diagnostic, CompilationUnitSyntax root, HashSet removeCollection) + { + var usingNode = GetTypeSyntaxNode(diagnostic, root); + if (usingNode!=null) + { + var usingNodes = (from usingDeclaration in root.Usings + where usingDeclaration.Name != null && usingDeclaration.Name.ToString().StartsWith(usingNode.Name!.ToString()) + select usingDeclaration).ToList(); + + removeCollection.UnionWith(usingNodes); + DefaultUsing.Remove(usingNodes.Select(item => item.Name!.ToString())); + } + } + } +} + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaStringExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaStringExtension.cs new file mode 100644 index 00000000..144a8430 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaStringExtension.cs @@ -0,0 +1,12 @@ +public static class NatashaStringExtension +{ + + public static string ToReadonlyScript(this string field) + { + + return $"Unsafe.AsRef({field})"; + + } + +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaTypeExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaTypeExtension.cs new file mode 100644 index 00000000..89050c91 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Extension/NatashaTypeExtension.cs @@ -0,0 +1,37 @@ +using System; + + +public static class NatashaTypeExtension +{ + + public static Delegate GetDelegateFromType(this Type type, string methodName, Type delegateType, object? target = null) + { + var info = type.GetMethod(methodName); + try + { + if (info == null) + { + throw new Exception($"未从{type.FullName}中反射出{methodName}方法!"); + } + return info.CreateDelegate(delegateType, target); + + } + catch (Exception ex) + { + + NatashaException exception = new($"类型为 {type.FullName} 的 {methodName} 方法无法转成委托 {delegateType.Name}!错误信息:{ex.Message}") + { + ErrorKind = NatashaExceptionKind.Delegate + }; + throw exception; + + } + + } + public static T GetDelegateFromType(this Type type, string methodName, object? target = null) where T : Delegate + { + return (T)GetDelegateFromType(type, methodName, typeof(T), target); + } + +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/NatashaManagement.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/NatashaManagement.cs new file mode 100644 index 00000000..1ab34646 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/NatashaManagement.cs @@ -0,0 +1,65 @@ +using System; +using System.Reflection; + +public static partial class NatashaManagement +{ +#if MULTI + /// + /// 和 NatashaInitializer.Preheating(); 一样 + /// + public static void Preheating( + Func? excludeReferencesFunc = null, + bool useRuntimeUsing = false, + bool useRuntimeReference = false) + { + NatashaInitializer.Preheating(excludeReferencesFunc, useRuntimeUsing, useRuntimeReference); + } + public static void Preheating( + bool useRuntimeUsing = false, + bool useRuntimeReference = false) + { + NatashaInitializer.Preheating(null, useRuntimeUsing, useRuntimeReference); + } +#else + public static void Preheating( + Func? excludeReferencesFunc = null) + { + NatashaInitializer.Preheating(excludeReferencesFunc); + } +#endif + + + /// + /// 增加全局 Using 引用,其他编译将默认添加该 Using + /// 例如: AddGlobalUsing("System.IO"); + /// + /// + public static void AddGlobalUsing(params string[] @namespaces) + { + DefaultUsing.AddUsing(@namespaces); + } + + /// + /// 增加全局 Using 引用,其他编译将默认添加该 Using + /// 例如: AddGlobalUsing("System.IO"); + /// + /// + public static void AddGlobalUsing(params Assembly[] @namespaces) + { + foreach (var item in @namespaces) + { + DefaultUsing.AddUsing(item); + } + } + + /// + /// 移除全局 Using 引用 + /// 例如: RemoveGlobalUsing("System.IO"); + /// + /// + public static void RemoveGlobalUsing(params string[] @namespaces) + { + DefaultUsing.Remove(@namespaces); + } +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/NatashaReferencePathsHelper.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/NatashaReferencePathsHelper.cs new file mode 100644 index 00000000..782fc2fd --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/NatashaReferencePathsHelper.cs @@ -0,0 +1,80 @@ + +using Microsoft.CodeAnalysis; +using Microsoft.Extensions.DependencyModel; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.PortableExecutable; + +public static class NatashaReferencePathsHelper +{ + public static IEnumerable? GetReferenceFiles(Func excludeReferencesFunc) + { + + + IEnumerable? paths = null; + try + { + paths = DependencyContext.Default? + .CompileLibraries.SelectMany(cl => cl.ResolveReferencePaths().Where(asmPath => + { + try + { + //#ISSUE:178 + using var peStream = File.OpenRead(asmPath); + PEReader pEReader = new PEReader(peStream); + if (!pEReader.HasMetadata) + { + return false; + } + + var asmName = AssemblyName.GetAssemblyName(asmPath); + return !excludeReferencesFunc(asmName, asmName.Name); + } + catch + { + return false; + } + })).ToList(); + } + catch + { + + } + + if (paths == null || !paths.Any()) + { + var refsFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "refs"); + if (Directory.Exists(refsFolder)) + { + paths = Directory.GetFiles(refsFolder); + } + + refsFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ref"); + if (Directory.Exists(refsFolder)) + { + if (paths != null) + { + + var tempPaths = Directory.GetFiles(refsFolder); + if (tempPaths != null && tempPaths!.Count() > 0) + { + paths = paths.Concat(tempPaths); + } + + } + else + { + + paths = Directory.GetFiles(refsFolder); + + } + } + } + return paths; + + } +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Reverser/AvailableNameReverser.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Reverser/AvailableNameReverser.cs new file mode 100644 index 00000000..c6dbf204 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Reverser/AvailableNameReverser.cs @@ -0,0 +1,25 @@ +using System; + +namespace Natasha.CSharp.Reverser +{ + /// + /// 文件可用名反解 + /// + public class AvailableNameReverser + { + + /// + /// 根据类型获取可用名 + /// + /// 类型 + /// + public static string GetAvailableName(Type type) + { + + return type.GetDevelopName().Replace('<', '_').Replace('>', '_').Replace(',', '_').Replace("[", "@").Replace("]", "@"); + + } + + } + +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Reverser/TypeNameReverser.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Reverser/TypeNameReverser.cs new file mode 100644 index 00000000..5cf48573 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Reverser/TypeNameReverser.cs @@ -0,0 +1,240 @@ +using System; +using System.Text; + +namespace Natasha.CSharp.Reverser +{ + /// + /// 类名反解器 + /// + public static class TypeNameReverser + { + //private static readonly Func _nullableAttrCheck; + + //static TypeNameReverser() + //{ + // _nullableAttrCheck = (name) => + // { + // return name.Contains("MaybeNullAttribute") + // || name.Contains("AllowNullAttribute") + // //|| name.Contains("NullableAttribute"); + // || name.Contains("NullableContextAttribute"); + // }; + + //} + + /// + /// 类名反解 + /// + /// 类型 + /// + internal static string ReverseFullName(Type type, bool ignoreFlag = false) + { + + string fatherString = string.Empty; + //外部类处理 + if (type.DeclaringType != null && type.FullName != null) + { + fatherString = ReverseFullName(type.DeclaringType, ignoreFlag) + "."; + } + + + //后缀 + StringBuilder Suffix = new StringBuilder(); + + + //数组判别 + while (type!.HasElementType) + { + + if (type.IsArray) + { + + int count = type.GetArrayRank(); + + Suffix.Append("["); + for (int i = 0; i < count - 1; i++) + { + Suffix.Append(","); + } + Suffix.Append("]"); + + } + type = type.GetElementType()!; + + } + + + //泛型判别 + if (type.IsGenericType) + { + + StringBuilder result = new StringBuilder(); + if (string.IsNullOrEmpty(fatherString) && !string.IsNullOrEmpty(type.Namespace) && !string.IsNullOrEmpty(type.FullName)) + { + result.Append(type.Namespace + "."); + } + result.Append($"{type.Name.Split('`')[0]}<"); + bool HasWriteArguments = false; + + + if (type.GenericTypeArguments.Length > 0) + { + + HasWriteArguments = true; + result.Append(ReverseFullName(type.GenericTypeArguments[0], ignoreFlag)); + for (int i = 1; i < type.GenericTypeArguments.Length; i++) + { + + result.Append(','); + result.Append(ReverseFullName(type.GenericTypeArguments[i], ignoreFlag)); + + } + + } + if (!HasWriteArguments) + { + + var types = ((System.Reflection.TypeInfo)type).GenericTypeParameters; + if (types.Length > 0) + { + + if (!ignoreFlag) + { + result.Append(ReverseFullName(types[0], ignoreFlag)); + } + + for (int i = 1; i < types.Length; i++) + { + + result.Append(','); + if (!ignoreFlag) + { + result.Append(ReverseFullName(types[i], ignoreFlag)); + } + + } + + } + + } + + + result.Append('>'); + result.Append(Suffix); + return fatherString + result.ToString(); + + } + else + { + + //特殊类型判别 + if (type == typeof(void)) + { + + return "void"; + + } + if (string.IsNullOrEmpty(fatherString) && !string.IsNullOrEmpty(type.Namespace) && !string.IsNullOrEmpty(type.FullName)) + { + return type.Namespace + "." + type.Name + Suffix; + } + return fatherString + type.Name + Suffix; + + } + } + + + /// + /// 类名反解 + /// + /// 类型 + /// + public static string ReverseTypeName(Type type) + { + + string fatherString = string.Empty; + //外部类处理 + if (type.DeclaringType != null && type.FullName != null) + { + fatherString = ReverseTypeName(type.DeclaringType) + "."; + } + + + //后缀 + StringBuilder Suffix = new StringBuilder(); + + + //数组判别 + while (type.HasElementType) + { + + if (type.IsArray) + { + + int count = type.GetArrayRank(); + + Suffix.Append("["); + for (int i = 0; i < count - 1; i++) + { + Suffix.Append(","); + } + Suffix.Append("]"); + + } + type = type.GetElementType()!; + + } + + //if (type.CustomAttributes.Any(item => _nullableAttrCheck(item.AttributeType.Name))) + //{ + // Suffix.Append('?'); + //} + + //泛型判别 + if (type.IsGenericType) + { + + StringBuilder result = new StringBuilder(); + result.Append($"{type.Name.Split('`')[0]}<"); + + if (type.GenericTypeArguments.Length > 0) + { + + result.Append(ReverseTypeName(type.GenericTypeArguments[0])); + for (int i = 1; i < type.GenericTypeArguments.Length; i++) + { + + result.Append(','); + result.Append(ReverseTypeName(type.GenericTypeArguments[i])); + + } + + } + + result.Append('>'); + result.Append(Suffix); + return fatherString + result.ToString(); + + } + else + { + + //特殊类型判别 + if (type == typeof(void)) + { + + return "void"; + + } + if (string.IsNullOrEmpty(fatherString) && !string.IsNullOrEmpty(type.Namespace) && !string.IsNullOrEmpty(type.FullName)) + { + return type.Name + Suffix; + } + return fatherString + type.Name + Suffix; + + } + + } + } + +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Reverser/TypeNatashaExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Reverser/TypeNatashaExtension.cs new file mode 100644 index 00000000..aec501a5 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Public/Reverser/TypeNatashaExtension.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using Natasha.CSharp.Reverser; + +public static class TypeNatashaExtension +{ + + /// + /// 判断是否为值类型,字符串类型,委托类型,Type类型,及委托的子类型其中之一 + /// + /// + /// + public static bool IsSimpleType(this Type type) + { + return type.IsValueType || + type == typeof(string) || + type.IsSubclassOf(typeof(Delegate)) || + type == typeof(Delegate) || + type.IsSubclassOf(typeof(MulticastDelegate)) || + type == typeof(MulticastDelegate) || + type == typeof(Type); + } + + + /// + /// 当前类是否实现了某接口 + /// + /// 要判断的类型 + /// 接口类型 + /// + public static bool IsImplementFrom(this Type type, Type iType) + { + return new HashSet(type.GetInterfaces()).Contains(iType); + } + + + /// + /// 当前类是否实现了某接口 + /// + /// 要判断的类型 + /// 接口类型 + /// + public static bool IsImplementFrom(this Type type) + { + return new HashSet(type.GetInterfaces()).Contains(typeof(T)); + } + + public static HashSet GetAllTypes(this Type type) + { + HashSet result = new(); + type.GetAllTypes(result); + return result; + } + /// + /// 获取与该类型相关的所有类型,例如 List => List<> / int32 + /// + /// + /// + internal static void GetAllTypes(this Type type, HashSet result) + { + + result.Add(type); + if (type.HasElementType) + { + var temp = type.GetElementType(); + temp?.GetAllTypes(result); + + } + else if (type.IsGenericType && type.FullName != null) + { + foreach (var item in type.GetGenericArguments()) + { + item.GetAllTypes(result); + } + } + + } + + + /// + /// 获取所有该类所含的类 List 则返回 List<> , Int32 + /// + /// + /// + public static HashSet GetAllTypes() + { + return typeof(T).GetAllTypes(); + } + + + + /// + /// 将类名替换成 文件名可使用的名字 + /// + /// + /// + public static string GetAvailableName(this Type type) + { + return AvailableNameReverser.GetAvailableName(type); + } + + + /// + /// 获取运行时完整类名 + /// 例如: System.Collections.Generic + /// 例如: System.Collections.Generic + /// + /// + /// + public static string GetDevelopName(this Type type) + { + return TypeNameReverser.ReverseFullName(type); + } + + + /// + /// 获取运行时类名, 例如: List + /// + /// + /// + public static string GetRuntimeName(this Type type) + { + return TypeNameReverser.ReverseTypeName(type); + } + + + /// + /// 获取运行时类名,无标识 + /// System.Collections.Generic.List<> + /// + /// + /// + public static string GetDevelopNameWithoutFlag(this Type type) + { + return TypeNameReverser.ReverseFullName(type, true); + } + + + // + /// 制作范型类型 + /// + /// IType + /// T,S,D + /// + public static Type With(this Type type, params Type[] types) + { + return type.MakeGenericType(types); + } + +} + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.Compile.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.Compile.cs new file mode 100644 index 00000000..d6c9db5c --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.Compile.cs @@ -0,0 +1,156 @@ +#if !MULTI +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Emit; +using Natasha.CSharp.Component; +using Natasha.CSharp.Component.Exception; +using Natasha.CSharp.Extension.Inner; +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +/// +/// 程序集编译构建器 - 编译选项 +/// +public sealed partial class AssemblyCSharpBuilder +{ + + /// + /// 流编译成功之后触发的事件 + /// + public event Action? CompileSucceedEvent; + + + /// + /// 流编译失败之后触发的事件 + /// + public event Action>? CompileFailedEvent; + + + public CSharpCompilation GetAvailableCompilation() + { +#if DEBUG + Stopwatch stopwatch = new(); + stopwatch.Start(); +#endif + + var options = _compilerOptions.GetCompilationOptions(); + var references = NatashaReferenceCache.GetReferences(); + _compilation = CSharpCompilation.Create(AssemblyName, SyntaxTrees, references, options); + +#if DEBUG + stopwatch.RestartAndShowCategoreInfo("[Compiler]", "获取编译单元", 2); +#endif + + + if (EnableSemanticHandler) + { + foreach (var item in _semanticAnalysistor) + { + _compilation = item(this, _compilation, _semanticCheckIgnoreAccessibility); + } + } + +#if DEBUG + stopwatch.RestartAndShowCategoreInfo("[Semantic]", "语义处理", 2); +#endif + return _compilation; + } + + + /// + /// 将 SyntaxTrees 中的语法树编译到程序集.如果不成功会抛出 NatashaException. + /// + /// + /// + /// + /// + /// //程序集的域加载行为, 该行为决定了编译后的程序集随着依赖加载到域中的处理结果. + /// //和加载插件原理相同. + /// builder.CompileWithAssemblyLoadBehavior(enum); + /// + /// //编译单元的引用加载行为, 遇到同名不同版本的引用该如何处理. + /// builder.CompileWithReferenceLoadBehavior(enum); + /// builder.CompileWithReferencesFilter(func); + /// + /// + /// + /// + public Assembly GetAssembly() + { + + if (_compilation == null) + { + _compilation = GetAvailableCompilation(); + } +#if DEBUG + Stopwatch stopwatch = new(); + stopwatch.Start(); +#endif + Stream dllStream; + Stream pdbStream; + Stream? xmlStream = null; + if (DllFilePath != string.Empty) + { + dllStream = File.Create(DllFilePath); + } + else + { + dllStream = new MemoryStream(); + } + + if (PdbFilePath != string.Empty) + { + pdbStream = File.Create(PdbFilePath); + } + else + { + pdbStream = new MemoryStream(); + } + + if (XmlFilePath != string.Empty) + { + xmlStream = File.Create(XmlFilePath); + } + + var compileResult = _compilation.Emit( + dllStream, + pdbStream: pdbStream, + xmlDocumentationStream: xmlStream, + options: new EmitOptions(pdbFilePath: PdbFilePath == string.Empty ? null : PdbFilePath, debugInformationFormat: DebugInformationFormat.PortablePdb)); + + + LogCompilationEvent?.Invoke(_compilation.GetNatashaLog()); + + dllStream.Dispose(); + pdbStream?.Dispose(); + xmlStream?.Dispose(); + + Assembly? assembly = null; + if (compileResult.Success) + { + assembly = Assembly.LoadFrom(DllFilePath); + DefaultUsing.AddUsing(assembly); + NatashaReferenceCache.AddReference(DllFilePath); + CompileSucceedEvent?.Invoke(_compilation, assembly!); + } + +#if DEBUG + stopwatch.StopAndShowCategoreInfo("[ Emit ]", "编译时长", 2); +#endif + + if (!compileResult.Success) + { + CompileFailedEvent?.Invoke(_compilation, compileResult.Diagnostics); + throw NatashaExceptionAnalyzer.GetCompileException(_compilation, compileResult.Diagnostics); + } + + return assembly!; + } + +} + +#endif + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.Ouput.Single.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.Ouput.Single.cs new file mode 100644 index 00000000..0a1e5429 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.Ouput.Single.cs @@ -0,0 +1,37 @@ +#if !MULTI +using System; +using System.IO; + +/// +/// 程序集编译构建器-输出 +/// +public sealed partial class AssemblyCSharpBuilder +{ + + public AssemblyCSharpBuilder UseNatashaFileOut(string? folder = null) + { + if (folder == null) + { + if (OutputFolder == GlobalOutputFolder) + { + OutputFolder = Path.Combine(GlobalOutputFolder,"Default"); + } + } + else + { + OutputFolder = folder; + } + if (!Directory.Exists(OutputFolder)) + { + Directory.CreateDirectory(OutputFolder); + } + DllFilePath = Path.Combine(OutputFolder, $"{AssemblyName}.dll"); + PdbFilePath = Path.Combine(OutputFolder, $"{AssemblyName}.pdb"); + XmlFilePath = Path.Combine(OutputFolder, $"{AssemblyName}.xml"); + return this; + } + +} +#endif + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.Syntax.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.Syntax.cs new file mode 100644 index 00000000..ba76625c --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.Syntax.cs @@ -0,0 +1,29 @@ +#if !MULTI +/// +/// 程序集编译构建器 - 语法树相关 +/// +public sealed partial class AssemblyCSharpBuilder +{ + /// + /// 注入代码并拼接using + /// + /// 脚本代码 + /// using 拼接行为 + /// + public AssemblyCSharpBuilder Add(string script, UsingLoadBehavior usingLoadBehavior) + { + switch (usingLoadBehavior) + { + case UsingLoadBehavior.WithDefault: + case UsingLoadBehavior.WithAll: + return AddScript(DefaultUsing.UsingScript + script); + default: + return AddScript(script); + } + } +} +#endif + + + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.cs new file mode 100644 index 00000000..dfd06dbf --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/CompileUnit/AssemblyCSharpBuilder.cs @@ -0,0 +1,42 @@ +#if !MULTI +using Natasha.CSharp.Compiler.SemanticAnalaysis; +using System; +using System.Runtime.CompilerServices; + +/// +/// 程序集编译构建器 +/// 默认开启语义过滤 +/// 默认域内引用优先 +/// 默认GUID作为程序集名 +/// +[assembly: InternalsVisibleTo("FrameworkFunctionUT, PublicKey=002400000480000094000000060200000024000052534131000400000100010069acb31dd0d9918441d6ed2b49cd67ae17d15fd6ded4ccd2f99b4a88df8cddacbf72d5897bb54f406b037688d99f482ff1c3088638b95364ef614f01c3f3f2a2a75889aa53286865463fb1803876056c8b98ec57f0b3cf2b1185de63d37041ba08f81ddba0dccf81efcdbdc912032e8d2b0efa21accc96206c386b574b9d9cb8")] +public sealed partial class AssemblyCSharpBuilder +{ + + public AssemblyCSharpBuilder():this(Guid.NewGuid().ToString("N")) + { + + } + public AssemblyCSharpBuilder(string assemblyName) + { + EnableSemanticHandler = true; + _semanticCheckIgnoreAccessibility = true; + OutputFolder = GlobalOutputFolder; + _parsingBehavior = UsingLoadBehavior.None; + _compilerOptions = new(); + _semanticAnalysistor = new() + { + UsingAnalysistor._usingSemanticDelegate + }; + SyntaxTrees = new(); + AssemblyName = assemblyName; + DllFilePath = string.Empty; + PdbFilePath = string.Empty; + XmlFilePath = string.Empty; + this.UseNatashaFileOut(); + } + +} +#endif + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/Component/NatashaReferenceCache.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/Component/NatashaReferenceCache.cs new file mode 100644 index 00000000..403c0ffb --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/Component/NatashaReferenceCache.cs @@ -0,0 +1,72 @@ +#if !MULTI +using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace Natasha.CSharp.Component +{ + //与元数据相关 + //数据值与程序集及内存相关 + public static class NatashaReferenceCache + { + /// + /// 存放内存流编译过来的程序集与引用 + /// + private static readonly HashSet _referenceCache; + static NatashaReferenceCache() + { + _referenceCache = new(); + } + + public static int Count { get { return _referenceCache.Count; } } + + public static void AddReference(string path) + { + DefaultUsing.AddUsing(Assembly.ReflectionOnlyLoadFrom(path)); + AddReference(MetadataReference.CreateFromFile(path)); + } + public static void AddReference(Assembly assembly) + { + DefaultUsing.AddUsing(assembly); + if (!string.IsNullOrEmpty(assembly.Location)) + { + AddReference(MetadataReference.CreateFromFile(assembly.Location)); + } + } + public static void RemoveReference(string path) + { + RemoveReference(MetadataReference.CreateFromFile(path)); + } + public static void RemoveReference(Assembly assembly) + { + if (!string.IsNullOrEmpty(assembly.Location)) + { + RemoveReference(MetadataReference.CreateFromFile(assembly.Location)); + } + } + + private static void RemoveReference(MetadataReference reference) + { + lock (_referenceCache) + { + _referenceCache.Remove(reference); + } + } + + private static void AddReference(MetadataReference reference) + { + lock (_referenceCache) + { + _referenceCache.Add(reference); + } + } + + public static IEnumerable GetReferences() + { + return _referenceCache; + } + + } +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/Extension/Inner/CSharpCompilationExtension.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/Extension/Inner/CSharpCompilationExtension.cs new file mode 100644 index 00000000..311ed16b --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/Extension/Inner/CSharpCompilationExtension.cs @@ -0,0 +1,126 @@ +#if !MULTI +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Natasha.CSharp.Extension.Inner +{ + + internal static class CSharpCompilationExtension + { + internal static NatashaCompilationLog GetNatashaLog(this CSharpCompilation compilation) + { + NatashaCompilationLog natashaCompilation = new(); + natashaCompilation.AddCompilationInfo("AssemblyName", compilation.AssemblyName ?? string.Empty); + natashaCompilation.AddCompilationInfo("Language", compilation.Language); + natashaCompilation.AddCompilationInfo("LanguageVersion", compilation.LanguageVersion.ToString()); + natashaCompilation.AddCompilationInfo("SyntaxTreeCount", compilation.SyntaxTrees.Length.ToString()); + natashaCompilation.AddCompilationInfo("ReferencesCount", compilation.References.Count().ToString()); + var errors = compilation.GetDiagnostics(); + if (errors.Length > 0) + { + Dictionary> syntaxCache = new(); + foreach (var item in compilation.GetDiagnostics()) + { + if (item.Location.SourceTree != null) + { + var tree = item.Location.SourceTree; + if (!syntaxCache.ContainsKey(tree)) + { + syntaxCache[tree] = new List(); + } + syntaxCache[tree].Add(item); + } + } + natashaCompilation.HasError = true; + foreach (var item in syntaxCache) + { + var codeText = item.Key.ToString(); + StringBuilder errorMessage = new(); + foreach (var error in item.Value) + { + var span = error.Location.GetLineSpan(); + errorMessage.AppendLine($"第{span.StartLinePosition.Line + 1}行,第{span.StartLinePosition.Character}个字符: 内容【{GetErrorMessage(codeText, error.Location.GetLineSpan())}】 {error.GetMessage()}"); + } + natashaCompilation.AddMessage(item.Value.Count, AddLineNumber(codeText), errorMessage.ToString()); + } + } + else + { + natashaCompilation.HasError = false; + foreach (var item in compilation.SyntaxTrees) + { + natashaCompilation.AddMessage(0, AddLineNumber(item.ToString()), item.GetFirstOopName()); + } + } + return natashaCompilation; + } + private static string GetErrorMessage(string content, FileLinePositionSpan linePositionSpan) + { + + var start = linePositionSpan.StartLinePosition; + var end = linePositionSpan.EndLinePosition; + + + var arrayLines = content.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + var currentErrorLine = arrayLines[start.Line]; + + + if (start.Line == end.Line) + { + + if (start.Character == end.Character) + { + + return currentErrorLine.Trim(); + + } + else + { + + return currentErrorLine.Substring(start.Character, end.Character - start.Character).Trim(); + + } + + } + else + { + + StringBuilder builder = new(); + builder.AppendLine(currentErrorLine.Substring(start.Character, currentErrorLine.Length-start.Character)); + for (int i = start.Line; i < end.Line - 1; i += 1) + { + + builder.AppendLine(arrayLines[i]); + + } + currentErrorLine = arrayLines[end.Line]; + builder.AppendLine(currentErrorLine.Substring(0, end.Character)); + return builder.ToString(); + + } + + } + private static string AddLineNumber(string code) + { + + StringBuilder builder = new(); + var arrayLines = code.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + for (int i = 0; i < arrayLines.Length; i += 1) + { + + builder.AppendLine($"{i + 1}\t{arrayLines[i]}"); + + } + return builder.ToString(); + + } + } + +} +#endif + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/NatashaInitializer.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/NatashaInitializer.cs new file mode 100644 index 00000000..3b3c8b4d --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/NatashaInitializer.cs @@ -0,0 +1,99 @@ +#if !MULTI +using Microsoft.CodeAnalysis; +using Microsoft.Extensions.DependencyModel; +using Natasha.CSharp; +using Natasha.CSharp.Component; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +public static class NatashaInitializer +{ + private static readonly object _lock = new(); + + private static bool _isCompleted = false; + public static void Preheating(Func? excludeReferencesFunc = null, bool useLowMemeory = false) + { + + if (!_isCompleted) + { + lock (_lock) + { + if (_isCompleted) + { + return; + } + + _isCompleted = true; + + if (excludeReferencesFunc == null) + { + excludeReferencesFunc = (_, _) => false; + } +#if DEBUG + Stopwatch stopwatch = new(); + stopwatch.Start(); +#endif + + DefaultUsing.SetDefaultUsingFilter(excludeReferencesFunc); + IEnumerable? paths = NatashaReferencePathsHelper.GetReferenceFiles(excludeReferencesFunc); + + +#if DEBUG + stopwatch.RestartAndShowCategoreInfo("[Reference]", "过滤初始化引用", 1); +#endif + if (paths != null && paths.Count() > 0) + { + ResolverMetadata(paths); +#if DEBUG + stopwatch.RestartAndShowCategoreInfo("[ Domain ]", "默认信息初始化", 1); +#endif + + AssemblyCSharpBuilder cSharpBuilder = new(); + cSharpBuilder.UseNatashaFileOut(); + cSharpBuilder.ConfigCompilerOption(item => item.AddSupperess("CS8019").UseSuppressReportor(false)); + cSharpBuilder.EnableSemanticHandler = true; + cSharpBuilder.Add(DefaultUsing.UsingScript + "public class A{}", UsingLoadBehavior.WithDefault); + var assembly = cSharpBuilder.GetAssembly(); + +#if DEBUG + stopwatch.StopAndShowCategoreInfo("[FirstCompile]", "初始化编译", 1); +#endif + } + + } + } + + } + private static void ResolverMetadata(IEnumerable paths) + { + + var result = Parallel.ForEach(paths, (path) => + { + //Assembly? assembly = null; + try + { + Assembly assembly = Assembly.ReflectionOnlyLoadFrom(path); + NatashaReferenceCache.AddReference(path); + DefaultUsing.AddUsingWithoutCheck(assembly); + } + catch + { + //Console.WriteLine(assembly?.FullName); + } + }); + while (!result.IsCompleted) + { + Thread.Sleep(100); + } + DefaultUsing.ReBuildUsingScript(); + + } + +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/NatashaManagement.cs b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/NatashaManagement.cs new file mode 100644 index 00000000..a6de7239 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/SingleDomain/NatashaManagement.cs @@ -0,0 +1,78 @@ +#if !MULTI +using Natasha.CSharp.Component; +using System; +using System.Reflection; + +public static partial class NatashaManagement +{ + + + /// + /// 增加元数据引用,编译需要元数据支持. + /// + /// 程序集 + /// + public static bool AddGlobalReference(Assembly assembly) + { + NatashaReferenceCache.AddReference(assembly); + return true; + } + /// + /// 增加元数据引用,编译需要元数据支持. + /// + /// 类型 + /// + public static bool AddGlobalReference(Type type) + { + if (type.Assembly.IsDynamic || type.Assembly.Location == null) + { + return false; + } + NatashaReferenceCache.AddReference(type.Assembly); + return true; + } + /// + /// 增加元数据引用,编译需要元数据支持. + /// + /// 程序集路径 + /// + public static bool AddGlobalReference(string path) + { + NatashaReferenceCache.AddReference(path); + return true; + } + + /// + /// 移除元数据引用,编译需要元数据支持. + /// + /// 程序集 + /// + public static bool RemoveGlobalReference(Assembly assembly) + { + NatashaReferenceCache.AddReference(assembly); + return true; + } + + /// + /// 移除元数据引用,编译需要元数据支持. + /// + /// 类型 + /// + public static bool RemoveGlobalReference(Type type) + { + NatashaReferenceCache.AddReference(type.Assembly); + return true; + } + + /// + /// 移除元数据引用,编译需要元数据支持. + /// + /// 程序集路径 + /// + public static bool RemoveGlobalReference(string path) + { + NatashaReferenceCache.AddReference(path); + return true; + } +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Compiler/Targets/Project.Usings.targets b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Targets/Project.Usings.targets new file mode 100644 index 00000000..2f3edd5a --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Compiler/Targets/Project.Usings.targets @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/API/Builder/OopBuilder.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/API/Builder/OopBuilder.cs new file mode 100644 index 00000000..61637eb2 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/API/Builder/OopBuilder.cs @@ -0,0 +1,17 @@ +#if MULTI +using Natasha.CSharp.Template; + +namespace Natasha.CSharp.Builder +{ + + public partial class OopBuilder : UsingTemplate where T : OopBuilder, new() + { + protected internal void BuildTree() + { + LoadCurrentDomainUsing(); + AssemblyBuilder.Add(this.GetScript()); + } + } + +} +#endif diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/API/Level2/NHandler.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/API/Level2/NHandler.cs new file mode 100644 index 00000000..536b1605 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/API/Level2/NHandler.cs @@ -0,0 +1,42 @@ +#if MULTI +using Natasha.CSharp.Builder; +using System; + +namespace Natasha.CSharp +{ + public class NHandler : OopBuilder where T : OopBuilder , new() + { + + public NDelegate DelegateHandler + { + get { return NDelegate.UseDomain(AssemblyBuilder.Domain); } + } + + + public NClass ClassHandler + { + get { return NClass.UseDomain(AssemblyBuilder.Domain).Using(NamespaceScript); } + } + + + public NInterface InterfaceHandler + { + get { return NInterface.UseDomain(AssemblyBuilder.Domain).Using(NamespaceScript); } + } + + + public NEnum EnumHandler + { + get { return NEnum.UseDomain(AssemblyBuilder.Domain).Using(NamespaceScript); } + } + + + public Func Creator() + { + return DelegateHandler.Func($"return new {NameScript}();"); + } + + } + +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/API/Level3/NInstance.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/API/Level3/NInstance.cs new file mode 100644 index 00000000..6d8bb62e --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/API/Level3/NInstance.cs @@ -0,0 +1,33 @@ +#if MULTI +using System; + +namespace Natasha.CSharp +{ + public static class NInstance + { + + public static Func Creator() + { + + return NDelegate.UseDomain(typeof(T).GetDomain()).Func($"return new {typeof(T).GetDevelopName()}();"); + + } + + + + + public static Delegate Creator(Type type) + { + + return FastMethodOperator + .UseDomain(type.GetDomain()) + .Body($"return new {type.GetDevelopName()}();") + .Return(type) + .Compile(); + + } + + } + +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/Oop/UsingTemplate.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/Oop/UsingTemplate.cs new file mode 100644 index 00000000..4c53e039 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/Oop/UsingTemplate.cs @@ -0,0 +1,17 @@ +#if MULTI +namespace Natasha.CSharp.Template +{ + + public partial class UsingTemplate : FlagTemplate where T : UsingTemplate, new() + { + + + protected T LoadCurrentDomainUsing() + { + return Using(AssemblyBuilder.Domain.UsingRecorder._usings); + } + + } + +} +#endif diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/Standard/ComplierTemplate.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/Standard/ComplierTemplate.cs new file mode 100644 index 00000000..9d2e427a --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/Standard/ComplierTemplate.cs @@ -0,0 +1,125 @@ +#if MULTI +using System; + +namespace Natasha.CSharp.Template +{ + + public partial class CompilerTemplate : ALinkTemplate where T : CompilerTemplate, new() + { + + #region 指定编译器的域进行创建 + + /// + /// 用传入的编译单元域来初始化编译单元. + /// + /// + public static T UseCompiler(AssemblyCSharpBuilder builder, Action? option = default) + { + + return UseDomain(builder.Domain, option); + + } + #endregion + #region 指定字符串域创建以及参数 + + /// + /// 创建一个域来初始化编译单元. 此方法创建的实例 instance.
+ ///
+ /// + /// + /// + /// + /// //使用 NoGlobalUsing 来禁用全局 using 覆盖.(默认开启) + /// instance.NoGlobalUsing(); + /// + /// //使用 NotLoadDomainUsing 来禁用域内 using 覆盖.(默认开启) + /// instance.NotLoadDomainUsing(); + /// + /// //使用 ConfigBuilder 方法来配置编译单元. + /// instance.ConfigBuilder(bld=>bld); + /// + /// //使用 ConfigCompilerOption 方法来配置编译选项. + /// bld=>bld.ConfigCompilerOption(opt=>opt.xxx); + /// + /// //使用 ConfigSyntaxOptions 方法来配置语法选项 + /// bld=>bld.ConfigSyntaxOptions(opt=>opt.xxx). + /// + /// + /// + /// + public static T CreateDomain(string domainName, Action? option = default) + { + + if (domainName.ToLower() == "default") + { + return UseDomain(NatashaReferenceDomain.DefaultDomain, option); + } + else + { + return UseDomain(DomainManagement.Create(domainName), option); + } + + } + #endregion + #region 指定域创建以及参数 + + + /// + /// 使用作用域中的 Domain + /// + /// + public static T UseScope() + { + return new(); + } + + + + /// + /// 使用一个域来初始化编译单元 + /// + /// + public static T UseDomain(NatashaReferenceDomain domain, Action? option = default) + { + + T instance = new(); + instance.AssemblyBuilder.Domain = domain; + instance.OptionAction = option; + option?.Invoke(instance.AssemblyBuilder); + return instance; + + } + #endregion + #region Default 默认域创建以及参数 + + + /// + /// 使用默认域来初始化编译单元. + /// + /// + public static T DefaultDomain(Action? option = default) + { + + return UseDomain(NatashaReferenceDomain.DefaultDomain, option); + + } + + + #endregion + #region 随机域创建以及参数 + + /// + /// 使用随机域来初始化编译单元. + /// + /// + public static T RandomDomain(Action? option = default) + { + + return UseDomain(DomainManagement.Random(), option); + + } + #endregion + + } +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/Standard/GlobalUsingTemplate.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/Standard/GlobalUsingTemplate.cs new file mode 100644 index 00000000..11974d52 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/MultiDomain/Standard/GlobalUsingTemplate.cs @@ -0,0 +1,109 @@ +#if MULTI +using System.Text; + +namespace Natasha.CSharp.Template +{ + /// + /// 记录模板 + /// + /// LINK返回的类型 + public partial class GlobalUsingTemplate : ScriptTemplate where T : GlobalUsingTemplate, new() + { + + public GlobalUsingTemplate() + { + _useGlobalUsing = true; + _autoLoadDomainUsing = true; + UsingRecorder = new(); + _script = new StringBuilder(200); + + } + + private bool _autoLoadDomainUsing; + public T LoadDomainUsing() + { + _autoLoadDomainUsing = true; + return Link; + } + public T NotLoadDomainUsing() + { + _autoLoadDomainUsing = false; + return Link; + } + + public StringBuilder GetUsingBuilder() + { +#if DEBUG + Stopwatch stopwatch = new(); + stopwatch.Start(); +#endif + var usingScript = new StringBuilder(); + + //如果用户想使用自定义的Using + if (!_useGlobalUsing) + { + + + foreach (var @using in UsingRecorder._usings) + { + + usingScript.AppendLine($"using {@using};"); + + } + //使用全局Using + if (_autoLoadDomainUsing) + { + foreach (var @using in AssemblyBuilder.Domain.UsingRecorder._usings) + { + if (!UsingRecorder.HasUsing(@using)) + { + + usingScript.AppendLine($"using {@using};"); + + } + } + } + + } + else + { + + //使用全局Using + if (_autoLoadDomainUsing && AssemblyBuilder.Domain.Name != "Default") + { + foreach (var @using in AssemblyBuilder.Domain.UsingRecorder._usings) + { + if (!DefaultUsing.HasElement(@using) && !UsingRecorder.HasUsing(@using)) + { + + usingScript.AppendLine($"using {@using};"); + + } + } + } + + //把当前域中的using全部加上 + foreach (var @using in UsingRecorder._usings) + { + + //如果全局已经存在using了,就不加了 + if (!DefaultUsing.HasElement(@using)) + { + + usingScript.AppendLine($"using {@using};"); + + } + + } + usingScript.Append(DefaultUsing.UsingScript); + + } +#if DEBUG + stopwatch.StopAndShowCategoreInfo("[using]", "using 组合", 3); +#endif + return usingScript; + } + } + +} +#endif \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/Natasha.CSharp.Template.csproj b/src/Natasha.CSharp/Natasha.CSharp.Template/Natasha.CSharp.Template.csproj new file mode 100644 index 00000000..127830ed --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/Natasha.CSharp.Template.csproj @@ -0,0 +1,41 @@ + + + + netstandard2.0;netcoreapp3.1;net5.0;net6.0;net7.0 + Natasha 的编译模板 + DotNetCore.Natasha.CSharp.Template + 升级到最新版. + Roslyn;IL;Script;Dynamic;Natasha;NMS;Template + true + 5.2.2.1 + 5.2.2.1 + 5.2.2.1 + + + + MULTI; + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/ConstraintBuilder.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/ConstraintBuilder.cs new file mode 100644 index 00000000..24c16ece --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/ConstraintBuilder.cs @@ -0,0 +1,90 @@ +using Natasha.CSharp.Template.Reverser; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Natasha.CSharp.Builder +{ + public class ConstraintBuilder + { + + private string _typeName; + private readonly HashSet _types; + private readonly HashSet _enums; + + public ConstraintBuilder() + { + _typeName = string.Empty; + _types = new HashSet(); + _enums = new HashSet(); + } + + public ConstraintBuilder SetType(string typeName) + { + _typeName = typeName; + return this; + } + + + public ConstraintBuilder Constraint(ConstraintFlags constraint) + { + + _enums.Add(constraint); + return this; + + } + + public ConstraintBuilder Constraint() + { + + return Constraint(typeof(TConstraint)); + + } + public ConstraintBuilder Constraint(Type type) + { + + _types.Add(type); + return this; + + } + + public string GetScript() + { + + StringBuilder builder = new StringBuilder(); + if (_types.Count > 0 || _enums.Count > 0) + { + + + foreach (var item in _enums) + { + if (item == ConstraintFlags.Class || item == ConstraintFlags.Struct) + { + + builder.Insert(0, GenericConstraintReverser.GetConstraint(item) + ","); + + } + else + { + + builder.Append(GenericConstraintReverser.GetConstraint(item) + ","); + + } + + } + foreach (var item in _types) + { + + builder.Append(item.GetDevelopName()+","); + + } + builder.Insert(0, $"where {_typeName} : "); + builder.Length -= 1; + } + return builder.ToString(); + + } + + + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/CtorBuilder.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/CtorBuilder.cs new file mode 100644 index 00000000..d6a0f4b0 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/CtorBuilder.cs @@ -0,0 +1,17 @@ +using Natasha.CSharp.Template; + +namespace Natasha.CSharp.Builder +{ + public class CtorBuilder : DelegateTemplate + { + + public CtorBuilder() + { + + Link = this; + NoUseType(); + + } + + } +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/DelegateBuilder.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/DelegateBuilder.cs new file mode 100644 index 00000000..e8032070 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/DelegateBuilder.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; + +namespace Natasha.CSharp.Builder +{ + + /// + /// 委托构建器,动态构建Func和Action委托 + /// + public class DelegateBuilder + { + + private static readonly Type[] FuncMaker; + private static readonly Type[] ActionMaker; + + static DelegateBuilder() + { + FuncMaker = new Type[9]; + FuncMaker[0] = typeof(Func<>); + FuncMaker[1] = typeof(Func<,>); + FuncMaker[2] = typeof(Func<,,>); + FuncMaker[3] = typeof(Func<,,,>); + FuncMaker[4] = typeof(Func<,,,,>); + FuncMaker[5] = typeof(Func<,,,,,>); + FuncMaker[6] = typeof(Func<,,,,,,>); + FuncMaker[7] = typeof(Func<,,,,,,,>); + FuncMaker[8] = typeof(Func<,,,,,,,,>); + + ActionMaker = new Type[9]; + ActionMaker[0] = typeof(Action); + ActionMaker[1] = typeof(Action<>); + ActionMaker[2] = typeof(Action<,>); + ActionMaker[3] = typeof(Action<,,>); + ActionMaker[4] = typeof(Action<,,,>); + ActionMaker[5] = typeof(Action<,,,,>); + ActionMaker[6] = typeof(Action<,,,,,>); + ActionMaker[7] = typeof(Action<,,,,,,>); + ActionMaker[8] = typeof(Action<,,,,,,,>); + } + + + + + /// + /// 获取函数委托 + /// + /// 泛型参数 + /// 返回类型 + /// 函数委托 + public static Type GetDelegate(Type[]? parametersTypes = null, Type? returnType = null) + { + if (returnType == null || returnType == typeof(void)) + { + return GetAction(parametersTypes); + } + + return GetFunc(returnType, parametersTypes); + } + + + + + /// + /// 根据类型动态生成Func委托 + /// + /// 返回类型 + /// 泛型类型 + /// Func委托类型 + public static Type GetFunc(Type returnType, params Type[]? parametersTypes) + { + if (parametersTypes == null || parametersTypes.Length == 0) + { + return FuncMaker[0].MakeGenericType(returnType); + } + + var list = new Type[parametersTypes.Length+1]; + for (int i = 0; i < parametersTypes.Length; i++) + { + list[i] = parametersTypes[i]; + } + list[parametersTypes.Length] = returnType; + return FuncMaker[parametersTypes.Length].MakeGenericType(list); + } + + + + + /// + /// 根据类型动态生成Action委托 + /// + /// 泛型参数类型 + /// Action委托类型 + public static Type GetAction(params Type[]? parametersTypes) + { + if (parametersTypes == null || parametersTypes.Length == 0) + { + return ActionMaker[0]; + } + + return ActionMaker[parametersTypes.Length].MakeGenericType(parametersTypes); + } + + } + +} \ No newline at end of file diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/FieldBuilder.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/FieldBuilder.cs new file mode 100644 index 00000000..ea6e9832 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/FieldBuilder.cs @@ -0,0 +1,16 @@ +using Natasha.CSharp.Template; + +namespace Natasha.CSharp.Builder +{ + + public class FieldBuilder : FieldTemplate + { + + public FieldBuilder() + { + Link = this; + } + + } + +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/MethodBuilder.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/MethodBuilder.cs new file mode 100644 index 00000000..ccf50400 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/MethodBuilder.cs @@ -0,0 +1,214 @@ +using Natasha.CSharp.Template; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Natasha.CSharp.Builder +{ + + public class MethodBuilder : MethodBuilder + { + + public MethodBuilder() + { + Link = this; + } + + } + + + public class MethodBuilder : DelegateTemplate where T : MethodBuilder, new() + { + + + public readonly OopBuilder OopHandler; + + public MethodBuilder() + { + OopHandler = new OopBuilder(); + Init(); + + } + + + + public T SkipInit() + { + + this.OopHandler.AssemblyBuilder.ConfigCompilerOption(item => item.RemoveIgnoreAccessibility()); + this.AttributeAppend(); + return Link; + } + + + + public T Using() + { + + OopHandler.Using(); + return Link; + + } + public T Using(Type @using) + { + + OopHandler.Using(@using); + return Link; + + } + public T Using(string @using) + { + + OopHandler.Using(@using); + return Link; + + } + + public T Using(HashSet @using) + { + + OopHandler.Using(@using); + return Link; + + } + public T Using(NamespaceConverter @using) + { + + OopHandler.Using(@using); + return Link; + + } + public T Using(NamespaceConverter[]? @using) + { + + OopHandler.Using(@using); + return Link; + + } + + + + + public virtual void Init() + { + + } + + + + + public T ClassOptions(Action acion) + { + + acion?.Invoke(OopHandler); + return Link; + + } + + + + + /// + /// 设置类名 + /// + /// 类名 + /// + public T ClassName(string className) + { + + OopHandler.Name(className); + return Link; + + } + + + + + /// + /// 设置命名空间 + /// + /// 命名空间字符串 + /// + public T Namesapce(string @namespace) + { + + OopHandler.Namespace(@namespace); + return Link; + + } + + + + + public Delegate Compile(object? target = null) + { +#if DEBUG + Stopwatch stopwatch = new(); + stopwatch.Start(); +#endif + OopHandler.AssemblyBuilder = this.AssemblyBuilder; + if (OopHandler.NamespaceScript == default) + { + OopHandler.HiddenNamespace(); + } + +#if DEBUG + stopwatch.StopAndShowCategoreInfo("[ Using ]", "Using填充耗时", 1); +#endif + OopHandler.BodyAppend(GetScript()); + AssemblyBuilder.Add(OopHandler.GetScript()); + var assembly = AssemblyBuilder.GetAssembly(); + return assembly.GetDelegateFromShortName(OopHandler.NameScript, NameScript, DelegateType, target); + + } + + + + + + public S Compile(object? target = null) where S : Delegate + { +#if DEBUG + Stopwatch stopwatch = new(); + stopwatch.Start(); +#endif + + OopHandler.AssemblyBuilder = this.AssemblyBuilder; + if (OopHandler.NamespaceScript == default) + { + OopHandler.HiddenNamespace(); + } +#if DEBUG + stopwatch.RestartAndShowCategoreInfo("[ Using ]", "Using填充耗时", 1); +#endif + //自动判别是否有手动指定方法参数,若没有则使用方法的参数 + var method = typeof(S).GetMethods()[0]!; + if (ParametersScript.Length == 0) + { + + Param(method); + + } + //自动判别返回值类型,若没有则使用委托的返回类型 + if (_type != method.ReturnType) + { + + this.Return(method.ReturnType); + + } +#if DEBUG + stopwatch.StopAndShowCategoreInfo("[Delegate]", "委托信息反解耗时", 1); + Console.WriteLine(); +#endif + //Mark : 11M Memory + OopHandler.BodyAppend(GetScript()); + AssemblyBuilder.Add(OopHandler.GetScript()); + var assembly = AssemblyBuilder.GetAssembly(); + return assembly.GetDelegateFromShortName(OopHandler.NameScript, NameScript, target); + + } + + } + +} diff --git a/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/OopBuilder.cs b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/OopBuilder.cs new file mode 100644 index 00000000..cb70cba8 --- /dev/null +++ b/src/Natasha.CSharp/Natasha.CSharp.Template/Public/API/Builder/OopBuilder.cs @@ -0,0 +1,500 @@ +using Natasha.CSharp.Extension.Inner; +using Natasha.CSharp.Template; +using System; +using System.Runtime.CompilerServices; + +namespace Natasha.CSharp.Builder +{ + + + public class OopBuilder : OopBuilder + { + + public OopBuilder() + { + Link = this; + } + + } + + + + public partial class OopBuilder : UsingTemplate where T : OopBuilder, new() + { + + //rivate readonly ConcurrentQueue