public void Load(string path) { try { string source = File.ReadAllText(path); var script = new ScriptData(GetId(path), path, source); scripts.Add(script.Id, script); debugger.AddScript(script); } catch (Exception ex) when(ex is FileNotFoundException || ex is PathTooLongException || ex is DirectoryNotFoundException || ex is IOException || ex is UnauthorizedAccessException) { string fileName = Path.GetFileName(path); throw new ScriptLoaderException($"Could not load '{fileName}': {ex.Message}", ex); } }
/// <summary> /// Adds a script to the execution queue. /// </summary> /// <param name="data">Script metadata</param> public void AddScript(ScriptData data) { // We need to parse the script ourselves (rather than using the Jint Engine constructor // that takes the source code as string) - in order to get the parser to assign our ID // as Source in the AST nodes' Location property. var parser = new JavaScriptParser(data.Script, new ParserOptions(data.Id) { AdaptRegexp = true, Loc = true, Tolerant = true }); data.Ast = parser.ParseScript(); scripts.Add(data); }
/// <summary> /// Adds breakpoint /// </summary> /// <param name="scriptId">ID of script</param> /// <param name="line">Line number (starting from 1)</param> /// <returns></returns> public bool TryAddBreakPoint(string scriptId, int line) { ScriptData script = GetScript(scriptId); if (script == null) { return(false); } var node = script.GetNodeAtLine(script.Ast, line); if (node == null) { return(false); } engine.BreakPoints.Add(new BreakPoint(node.Location.Source, node.Location.Start.Line, node.Location.Start.Column)); return(true); }
/// <summary> /// Executes the scripts in the debugger. /// </summary> public void Execute() { IsRunning = true; cts = new CancellationTokenSource(); try { // We run the debugger script execution on a separate thread. Why? // In a GUI app this is necessary, since Jint signals reaching a breakpoint // or step by triggering an event. When the event handler returns, script execution // continues. That means we'd be blocking the UI thread until the entire script is done // executing (if it ever is). // In a console app, it would be doable - do e.g. a Console.ReadLine when handling // the Step/Break event. However, we also want to be able to run a script without stepping, // and then stop/pause it if, for example, it ends up in an infinite loop. So, even in our // console app, the "UI thread" (Console read/write) must not be blocked while Jint is // executing the script. Task.Run(() => { try { int currentScriptIndex = 0; while (currentScriptIndex < scripts.Count) { ScriptData script = scripts[currentScriptIndex]; engine.Execute(script.Ast); currentScriptIndex++; } } catch (CancelExecutionException) { // Do nothing - the exception is just used to signal cancellation. } Done?.Invoke(); }, cts.Token); } finally { IsRunning = false; } }