diff options
| author | Roy Ben Shabat <Roy@twine-s.com> | 2020-12-30 15:12:05 +0000 |
|---|---|---|
| committer | Roy Ben Shabat <Roy@twine-s.com> | 2020-12-30 15:12:05 +0000 |
| commit | b88a5d8fbfb4da46ec0e8f64b79fd83013a9de38 (patch) | |
| tree | ea725abc39def99a755b041c13cba1fe0d594ddc /Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs | |
| parent | 1bdcaa9f51303bbff682507f31fb3b4414692ca4 (diff) | |
| parent | d33c19b3ac6803de4b5c8d475832efef131c1a45 (diff) | |
| download | Tango-b88a5d8fbfb4da46ec0e8f64b79fd83013a9de38.tar.gz Tango-b88a5d8fbfb4da46ec0e8f64b79fd83013a9de38.zip | |
Merged PR 6: Revert "Hope it is fine"
Revert "Hope it is fine"
Reverted commit `1344a54c`.
Diffstat (limited to 'Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs')
| -rw-r--r-- | Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs b/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs new file mode 100644 index 000000000..7500e404f --- /dev/null +++ b/Software/Visual_Studio/Scripting/Tango.Scripting.Basic/Project.cs @@ -0,0 +1,259 @@ +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using Tango.Core; +using Tango.Core.IO; +using Tango.Scripting.Core; +using System.IO; +using Tango.Core.Helpers; + +namespace Tango.Scripting.Basic +{ + public class Project<T> : ExtendedObject where T : IContext + { + private object _compileLock = new object(); + + public String ID { get; set; } + + private String _name; + public String Name + { + get { return _name; } + set { _name = value; RaisePropertyChangedAuto(); } + } + + private String _description; + public String Description + { + get { return _description; } + set { _description = value; RaisePropertyChangedAuto(); } + } + + private bool _isRunning; + [JsonIgnore] + public bool IsRunning + { + get { return _isRunning; } + set { _isRunning = value; RaisePropertyChangedAuto(); } + } + + private bool _isCompiling; + [JsonIgnore] + public bool IsCompiling + { + get { return _isCompiling; } + set { _isCompiling = value; RaisePropertyChangedAuto(); } + } + + public ApartmentState ApartmentState { get; set; } + + public ObservableCollection<ReferenceAssembly> ReferenceAssemblies { get; set; } + + public ObservableCollection<Script> Scripts { get; set; } + + [JsonIgnore] + public ObservableCollection<IScriptSource> AdditionalScripts + { + get + { + return Scripts.Where(x => !x.IsEntryPoint).Cast<IScriptSource>().ToObservableCollection(); + } + } + + [JsonIgnore] + public List<ScriptBreakPoint> BreakPoints { get; set; } + + public Project() + { + ID = Guid.NewGuid().ToString(); + + ApartmentState = ApartmentState.MTA; + + ReferenceAssemblies = new ObservableCollection<ReferenceAssembly>(); + + Scripts = new ObservableCollection<Script>(); + Scripts.CollectionChanged += (x, e) => { RaisePropertyChanged(nameof(AdditionalScripts)); }; + + BreakPoints = new List<ScriptBreakPoint>(); + } + + public Task<CompilationResult> Compile() + { + return Task.Factory.StartNew<CompilationResult>(() => + { + lock (_compileLock) + { + try + { + IsCompiling = true; + var result = new CompilationResult(); + var tempFolder = TemporaryManager.CreateFolder(Name + "_" + ID); + result.TemporaryProjectPath = tempFolder; + + String mainScriptCode = String.Empty; + + foreach (var script in Scripts) + { + script.LoadCount = 0; + script.LoadCharCount = 0; + String code = script.Code; + String codeFile = Path.Combine(tempFolder, script.Name); + + String loadingString = String.Empty; + + foreach (var file in Scripts.Where(x => !x.IsEntryPoint && script != x).Select(x => Path.Combine(tempFolder, x.Name))) + { + loadingString += $"#load \"{file}\"\n"; + script.LoadCount++; + } + + script.LoadCharCount += loadingString.Length; + + code = loadingString + code; + + int debugLinesLength = 0; + + foreach (var breakPoint in BreakPoints.Where(x => x.Script == script).OrderBy(x => x.LineNumber)) + { + var debugLine = $"context.BreakPoint(\"{script.Name}\",{breakPoint.LineNumber}"; + + foreach (var symbol in breakPoint.ContextSymbols) + { + debugLine += $",\"{symbol.Name}\",{symbol.Offset},{symbol.Length},{symbol.Name}"; + } + + debugLine += ");"; + + StringBuilder builder = new StringBuilder(code); + builder.Insert(breakPoint.LineStartOffset + loadingString.Length + debugLinesLength, debugLine); + code = builder.ToString(); + + debugLinesLength += debugLine.Length; + } + + if (!script.IsEntryPoint) + { + File.WriteAllText(codeFile, code); + } + else + { + code += Environment.NewLine + Environment.NewLine + "new Program().OnExecute(GlobalContext);"; + mainScriptCode = code; + } + } + + var scriptOptions = ScriptOptions.Default.WithReferences(LoadReferenceAssemblies()).WithEmitDebugInformation(true); + + var s = CSharpScript.Create<object>(mainScriptCode, scriptOptions, typeof(GlobalObject<T>)); + + result.Script = s; + + var compileResults = s.Compile(); + + GC.Collect(); + + foreach (var error in compileResults.Where(x => x.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error)) + { + CompilationError cError = new CompilationError(); + cError.File = System.IO.Path.GetFileName(error.Location.SourceTree.FilePath); + if (cError.File == String.Empty) + { + cError.File = Scripts.Single(x => x.IsEntryPoint).Name; + } + Script errorScript = Scripts.Single(x => x.Name == cError.File); + cError.Message = error.GetMessage(); + cError.Severity = error.Severity; + cError.Position = error.Location.SourceSpan.Start - (errorScript != null ? errorScript.LoadCharCount : 0); + var line = error.Location.GetMappedLineSpan(); + cError.Line = line.StartLinePosition.Line + 1 - (errorScript != null ? errorScript.LoadCount : 0); + cError.Column = line.StartLinePosition.Character + 1; + cError.Length = line.EndLinePosition.Character - line.StartLinePosition.Character; + result.Errors.Add(cError); + } + + return result; + } + catch (Exception) + { + throw; + } + finally + { + IsCompiling = false; + } + } + }); + } + + public async Task<ProjectSession<T>> Run(T context) + { + IsRunning = true; + var result = await Compile(); + + if (result.Errors.Count > 0) + { + throw new InvalidOperationException($"Cannot run project with the following compilation errors:\n{String.Join(Environment.NewLine, result.Errors.Select(x => x.Message))}"); + } + + Thread scriptThread = null; + ProjectSession<T> session = null; + + session = new ProjectSession<T>(this, () => + { + scriptThread.Abort(); + }); + + scriptThread = new Thread(() => + { + try + { + var runResult = result.Script.RunAsync(globals: new GlobalObject<T>() { GlobalContext = context }).Result; + IsRunning = false; + session.Completed(runResult.ReturnValue); + } + catch (ThreadAbortException) + { + + } + catch (Exception ex) + { + session.Failed(ex.InnerException); + } + finally + { + BreakPoints.Clear(); + GC.Collect(); + IsRunning = false; + } + }); + + scriptThread.SetApartmentState(ApartmentState); + scriptThread.IsBackground = true; + scriptThread.Start(); + + return session; + } + + public List<Assembly> LoadReferenceAssemblies() + { + List<Assembly> loadedAssemblies = new List<Assembly>(); + + foreach (var asm in ReferenceAssemblies) + { + loadedAssemblies.Add(asm.Load()); + } + + return loadedAssemblies; + } + } +} |
