Skip to content

Commit

Permalink
Merge pull request #1850 from UnderminersTeam/script-paths-and-except…
Browse files Browse the repository at this point in the history
…ions

Supply script file paths, better script exceptions
  • Loading branch information
Miepee authored Jul 29, 2024
2 parents da9ec09 + 2e46dff commit b1ad419
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 24 deletions.
3 changes: 2 additions & 1 deletion UndertaleModCli/Program.UMTLibInherited.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -586,7 +587,7 @@ public bool LintUMTScript(string path)
{
CancellationTokenSource source = new CancellationTokenSource(100);
CancellationToken token = source.Token;
CSharpScript.EvaluateAsync(File.ReadAllText(path), CliScriptOptions, this, typeof(IScriptInterface), token);
CSharpScript.EvaluateAsync(File.ReadAllText(path, Encoding.UTF8), CliScriptOptions.WithFilePath(path).WithFileEncoding(Encoding.UTF8), this, typeof(IScriptInterface), token);
}
catch (CompilationErrorException exc)
{
Expand Down
4 changes: 2 additions & 2 deletions UndertaleModCli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ private void RunCSharpFile(string path)
string lines;
try
{
lines = File.ReadAllText(path);
lines = File.ReadAllText(path, Encoding.UTF8);
}
catch (Exception exc)
{
Expand All @@ -739,7 +739,7 @@ private void RunCSharpCode(string code, string scriptFile = null)

try
{
CSharpScript.EvaluateAsync(code, CliScriptOptions, this, typeof(IScriptInterface)).GetAwaiter().GetResult();
CSharpScript.EvaluateAsync(code, CliScriptOptions.WithFilePath(scriptFile ?? "").WithFileEncoding(Encoding.UTF8), this, typeof(IScriptInterface)).GetAwaiter().GetResult();
ScriptExecutionSuccess = true;
ScriptErrorMessage = "";
}
Expand Down
91 changes: 70 additions & 21 deletions UndertaleModTool/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2476,23 +2476,23 @@ public void OpenCodeEntry(string name, int lineNum, UndertaleCodeEditor.CodeEdit
}
}

public string ProcessException(in Exception exc, in string scriptText)
public string ProcessException(in Exception exc)
{
List<int> excLineNums = new();
string excText = string.Empty;
// Collect all original trace lines that we want to parse
List<string> traceLines = new();
Dictionary<string, int> exTypesDict = null;

if (exc is AggregateException)
{
List<string> exTypes = new();

// Collect trace lines of inner exceptions, and track their exception type names
foreach (Exception ex in (exc as AggregateException).InnerExceptions)
{
traceLines.AddRange(ex.StackTrace.Split(Environment.NewLine));
exTypes.Add(ex.GetType().FullName);
}

// Create a mapping of each exception type to the number of its occurrences
if (exTypes.Count > 1)
{
exTypesDict = exTypes.GroupBy(x => x)
Expand All @@ -2503,23 +2503,35 @@ public string ProcessException(in Exception exc, in string scriptText)
}
else if (exc.InnerException is not null)
{
// Collect trace lines of single inner exception
traceLines.AddRange(exc.InnerException.StackTrace.Split(Environment.NewLine));
}

traceLines.AddRange(exc.StackTrace.Split(Environment.NewLine));

// Iterate over all lines in the stack trace, finding their line numbers and file names
List<(string SourceFile, int LineNum)> loadedScriptLineNums = new();
int expectedNumScriptTraceLines = 0;
try
{
foreach (string traceLine in traceLines)
{
if (traceLine.TrimStart()[..13] == "at Submission") // only stack trace lines from the script
// Only handle trace lines that come from a script
if (traceLine.TrimStart()[..13] == "at Submission")
{
int linePos = traceLine.IndexOf(":line ") + 6; // ":line ".Length = 6
if (linePos != (-1 + 6))
// Add to total count of expected script trace lines
expectedNumScriptTraceLines++;

// Get full path of the script file, within the line
string sourceFile = Regex.Match(traceLine, @"(?<=in ).*\.csx(?=:line \d+)").Value;
if (!File.Exists(sourceFile))
continue;

// Try to find line number from the line
const string pattern = ":line ";
int linePos = traceLine.IndexOf(pattern);
if (linePos > 0 && int.TryParse(traceLine[(linePos + pattern.Length)..], out int lineNum))
{
int lineNum = Convert.ToInt32(traceLine[linePos..]);
if (!excLineNums.Contains(lineNum))
excLineNums.Add(lineNum);
loadedScriptLineNums.Add((sourceFile, lineNum));
}
}
}
Expand All @@ -2530,30 +2542,68 @@ public string ProcessException(in Exception exc, in string scriptText)

int endOfPrevStack = excString.IndexOf("--- End of stack trace from previous location ---");
if (endOfPrevStack != -1)
excString = excString[..endOfPrevStack]; //keep only stack trace of the script
{
// Keep only stack trace of the script
excString = excString[..endOfPrevStack];
}

return $"An error occurred while processing the exception text.\nError message - \"{e.Message}\"\nThe unprocessed text is below.\n\n" + excString;
}

if (excLineNums.Count > 0) //if line number(s) is found
{
string[] scriptLines = scriptText.Split('\n');
string excLines = string.Join('\n', excLineNums.Select(n => $"Line {n}: {scriptLines[n].TrimStart(new char[] { '\t', ' ' })}"));
// Generate final exception text to show.
// If we found the expected number of script trace lines, then use them; otherwise, use the regular exception text.
string excText;
if (loadedScriptLineNums.Count == expectedNumScriptTraceLines)
{
// Read the code for the files to know what the code line associated with the stack trace is
Dictionary<string, List<string>> scriptsCode = new();
foreach ((string sourceFile, int _) in loadedScriptLineNums)
{
if (!scriptsCode.ContainsKey(sourceFile))
{
string scriptCode = null;
try
{
scriptCode = File.ReadAllText(sourceFile, Encoding.UTF8);
}
catch (Exception e)
{
string excString = exc.ToString();

return $"An error occurred while processing the exception text.\nError message - \"{e.Message}\"\nThe unprocessed text is below.\n\n" + excString;
}
scriptsCode.Add(sourceFile, scriptCode.Split('\n').ToList());
}
}

// Generate custom stack trace
string excLines = string.Join('\n', loadedScriptLineNums.Select(pair =>
{
string scriptName = Path.GetFileName(pair.SourceFile);
string scriptLine = scriptsCode[pair.SourceFile][pair.LineNum - 1]; // - 1 because line numbers start from 1
return $"Line {pair.LineNum} in script {scriptName}: {scriptLine}";
}));

if (exTypesDict is not null)
{
string exTypesStr = string.Join(",\n", exTypesDict.Select(x => $"{x.Key}{((x.Value > 1) ? " (x" + x.Value + ")" : string.Empty)}"));
excText = $"{exc.GetType().FullName}: One on more errors occured:\n{exTypesStr}\n\nThe current stacktrace:\n{excLines}";
}
else
{
excText = $"{exc.GetType().FullName}: {exc.Message}\n\nThe current stacktrace:\n{excLines}";
}
}
else
{
string excString = exc.ToString();

int endOfPrevStack = excString.IndexOf("--- End of stack trace from previous location ---");
if (endOfPrevStack != -1)
excString = excString[..endOfPrevStack]; //keep only stack trace of the script
{
// Keep only stack trace of the script
excString = excString[..endOfPrevStack];
}

excText = excString;
}
Expand Down Expand Up @@ -2586,8 +2636,7 @@ public async Task RunScript(string path)

private async Task RunScriptNow(string path)
{
string scriptText = $"#line 1 \"{path}\"\n" + File.ReadAllText(path);
Debug.WriteLine(path);
string scriptText = $"#line 1 \"{path}\"\n" + File.ReadAllText(path, Encoding.UTF8);

Dispatcher.Invoke(() => CommandBox.Text = "Running " + Path.GetFileName(path) + " ...");
try
Expand All @@ -2598,7 +2647,7 @@ private async Task RunScriptNow(string path)
ScriptPath = path;

string compatScriptText = Regex.Replace(scriptText, @"\bDecompileContext(?!\.)\b", "GlobalDecompileContext", RegexOptions.None);
object result = await CSharpScript.EvaluateAsync(compatScriptText, scriptOptions, this, typeof(IScriptInterface));
object result = await CSharpScript.EvaluateAsync(compatScriptText, scriptOptions.WithFilePath(path).WithFileEncoding(Encoding.UTF8), this, typeof(IScriptInterface));

if (FinishedMessageEnabled)
{
Expand All @@ -2624,7 +2673,7 @@ private async Task RunScriptNow(string path)
string excString = string.Empty;

if (!isScriptException)
excString = ProcessException(in exc, in scriptText);
excString = ProcessException(in exc);

await StopProgressBarUpdater();

Expand Down

0 comments on commit b1ad419

Please sign in to comment.