diff --git a/Jint/Engine.Advanced.cs b/Jint/Engine.Advanced.cs
index 61b8672a0d..2bd8aace37 100644
--- a/Jint/Engine.Advanced.cs
+++ b/Jint/Engine.Advanced.cs
@@ -28,7 +28,7 @@ public string StackTrace
return string.Empty;
}
- return _engine.CallStack.BuildCallStackString(lastSyntaxElement.Location);
+ return _engine.CallStack.BuildCallStackString(_engine, lastSyntaxElement.Location);
}
}
diff --git a/Jint/Native/Error/ErrorConstructor.cs b/Jint/Native/Error/ErrorConstructor.cs
index a03b7382d4..1db1d25cc2 100644
--- a/Jint/Native/Error/ErrorConstructor.cs
+++ b/Jint/Native/Error/ErrorConstructor.cs
@@ -80,7 +80,7 @@ public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
// If the current function is the ErrorConstructor itself (i.e. "throw new Error(...)" was called
// from script), exclude it from the stack trace, because the trace should begin at the throw point.
- return callStack.BuildCallStackString(lastSyntaxNode.Location, currentFunction == this ? 1 : 0);
+ return callStack.BuildCallStackString(_engine, lastSyntaxNode.Location, currentFunction == this ? 1 : 0);
}
}
}
diff --git a/Jint/Options.Extensions.cs b/Jint/Options.Extensions.cs
index 86822dcae0..115c5e5708 100644
--- a/Jint/Options.Extensions.cs
+++ b/Jint/Options.Extensions.cs
@@ -127,6 +127,17 @@ public static Options SetWrapObjectHandler(this Options options, Options.WrapObj
return options;
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Options SetBuildCallStackHandler(this Options options, Options.BuildCallStackDelegate buildCallStackHandler)
+ {
+ options.Interop.BuildCallStackHandler = buildCallStackHandler;
+ return options;
+ }
+
///
/// Sets the type converter to use.
///
diff --git a/Jint/Options.cs b/Jint/Options.cs
index c786d53399..0bf448e73e 100644
--- a/Jint/Options.cs
+++ b/Jint/Options.cs
@@ -2,6 +2,8 @@
using System.Globalization;
using System.Linq;
using System.Reflection;
+using System.Text;
+using Esprima;
using Jint.Native;
using Jint.Native.Object;
using Jint.Runtime;
@@ -27,6 +29,8 @@ public class Options
public delegate bool ExceptionHandlerDelegate(Exception exception);
+ public delegate void BuildCallStackDelegate(ref ValueStringBuilder valueStringBuilder, string shortDescription, Location location, List? arguments);
+
///
/// Execution constraints for the engine.
///
@@ -293,6 +297,42 @@ public class InteropOptions
///
public WrapObjectDelegate WrapObjectHandler { get; set; } = static (engine, target, type) => new ObjectWrapper(engine, target, type);
+ ///
+ ///
+ ///
+ public BuildCallStackDelegate BuildCallStackHandler { get; set; } = static (ref ValueStringBuilder sb, string shortDescription, Location loc, List? arguments) =>
+ {
+ sb.Append(" at");
+
+ if (!string.IsNullOrWhiteSpace(shortDescription))
+ {
+ sb.Append(' ');
+ sb.Append(shortDescription);
+ }
+
+ if (arguments is not null)
+ {
+ sb.Append(" (");
+ for (var index = 0; index < arguments.Count; index++)
+ {
+ if (index != 0)
+ {
+ sb.Append(", ");
+ }
+ sb.Append(arguments[index]);
+ }
+ sb.Append(')');
+ }
+
+ sb.Append(' ');
+ sb.Append(loc.Source);
+ sb.Append(':');
+ sb.Append(loc.End.Line.ToString(CultureInfo.InvariantCulture));
+ sb.Append(':');
+ sb.Append((loc.Start.Column + 1).ToString(CultureInfo.InvariantCulture)); // report column number instead of index
+ sb.Append(Environment.NewLine);
+ };
+
///
///
///
diff --git a/Jint/Pooling/ValueStringBuilder.cs b/Jint/Pooling/ValueStringBuilder.cs
index f5bd679329..3667b6489b 100644
--- a/Jint/Pooling/ValueStringBuilder.cs
+++ b/Jint/Pooling/ValueStringBuilder.cs
@@ -9,7 +9,7 @@
// ReSharper disable once CheckNamespace
namespace System.Text;
-internal ref struct ValueStringBuilder
+public ref struct ValueStringBuilder
{
private char[]? _arrayToReturnToPool;
private Span _chars;
diff --git a/Jint/Runtime/CallStack/JintCallStack.cs b/Jint/Runtime/CallStack/JintCallStack.cs
index d4b26749bd..373e9254a1 100644
--- a/Jint/Runtime/CallStack/JintCallStack.cs
+++ b/Jint/Runtime/CallStack/JintCallStack.cs
@@ -117,46 +117,23 @@ public override string ToString()
return string.Join("->", _stack.Select(static cse => cse.ToString()).Reverse());
}
- internal string BuildCallStackString(Location location, int excludeTop = 0)
+ internal string BuildCallStackString(Engine engine, Location location, int excludeTop = 0)
{
static void AppendLocation(
ref ValueStringBuilder sb,
string shortDescription,
in Location loc,
- in CallStackElement? element)
+ in CallStackElement? element,
+ Engine engine)
{
- sb.Append(" at");
-
- if (!string.IsNullOrWhiteSpace(shortDescription))
- {
- sb.Append(' ');
- sb.Append(shortDescription);
- }
+ List? arguments = null;
if (element?.Arguments is not null)
{
- // it's a function
- sb.Append(" (");
- for (var index = 0; index < element.Value.Arguments.Value.Count; index++)
- {
- if (index != 0)
- {
- sb.Append(", ");
- }
-
- var arg = element.Value.Arguments.Value[index];
- sb.Append(GetPropertyKey(arg));
- }
- sb.Append(')');
+ arguments = element.Value.Arguments.Value.Select(GetPropertyKey).ToList();
}
- sb.Append(' ');
- sb.Append(loc.Source);
- sb.Append(':');
- sb.Append(loc.End.Line.ToString(CultureInfo.InvariantCulture));
- sb.Append(':');
- sb.Append((loc.Start.Column + 1).ToString(CultureInfo.InvariantCulture)); // report column number instead of index
- sb.Append(System.Environment.NewLine);
+ engine.Options.Interop.BuildCallStackHandler.Invoke(ref sb, shortDescription, loc, arguments);
}
var builder = new ValueStringBuilder();
@@ -166,7 +143,7 @@ static void AppendLocation(
var element = index >= 0 ? _stack[index] : (CallStackElement?) null;
var shortDescription = element?.ToString() ?? "";
- AppendLocation(ref builder, shortDescription, location, element);
+ AppendLocation(ref builder, shortDescription, location, element, engine);
location = element?.Location ?? default;
index--;
@@ -176,7 +153,7 @@ static void AppendLocation(
element = index >= 0 ? _stack[index] : null;
shortDescription = element?.ToString() ?? "";
- AppendLocation(ref builder, shortDescription, location, element);
+ AppendLocation(ref builder, shortDescription, location, element, engine);
location = element?.Location ?? default;
index--;
diff --git a/Jint/Runtime/JavaScriptException.cs b/Jint/Runtime/JavaScriptException.cs
index d79b546062..68c8a6cd81 100644
--- a/Jint/Runtime/JavaScriptException.cs
+++ b/Jint/Runtime/JavaScriptException.cs
@@ -89,7 +89,7 @@ internal void SetCallstack(Engine engine, Location location, bool overwriteExist
var errObj = Error.IsObject() ? Error.AsObject() : null;
if (errObj is null)
{
- _callStack = engine.CallStack.BuildCallStackString(location);
+ _callStack = engine.CallStack.BuildCallStackString(engine, location);
return;
}
@@ -100,7 +100,7 @@ internal void SetCallstack(Engine engine, Location location, bool overwriteExist
}
else
{
- _callStack = engine.CallStack.BuildCallStackString(location);
+ _callStack = engine.CallStack.BuildCallStackString(engine, location);
errObj.FastSetProperty(CommonProperties.Stack._value, new PropertyDescriptor(_callStack, false, false, false));
}
}