2
0

CallStackTests.cs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. using Jint.Runtime;
  2. using SourceMaps;
  3. namespace Jint.Tests.PublicInterface;
  4. public class CallStackTests
  5. {
  6. [Fact]
  7. public void CanInjectTraceFunction()
  8. {
  9. var engine = new Engine();
  10. Assert.Empty(engine.Advanced.StackTrace);
  11. using var stringWriter = new StringWriter();
  12. engine.SetValue("console", new Console(engine, stringWriter));
  13. engine.Execute("function x() { console.trace(); }; function y() { x(); } y();");
  14. const string Expected = """
  15. Trace
  16. at trace (<anonymous>:1:16)
  17. at x (<anonymous>:1:16)
  18. at y (<anonymous>:1:51)
  19. at <anonymous>:1:58
  20. """;
  21. var actual = stringWriter.ToString();
  22. Assert.Equal(Expected, actual);
  23. }
  24. private class Console
  25. {
  26. private readonly Engine _engine;
  27. private readonly StringWriter _output;
  28. public Console(Engine engine, StringWriter output)
  29. {
  30. _engine = engine;
  31. _output = output;
  32. }
  33. public void Log(string message)
  34. {
  35. _output.WriteLine(message);
  36. }
  37. public void Trace()
  38. {
  39. _output.WriteLine($"Trace{Environment.NewLine}{_engine.Advanced.StackTrace}");
  40. }
  41. }
  42. [Fact]
  43. public void ShouldReturnTheSourceMapStack()
  44. {
  45. var sourceMap = SourceMapParser.Parse("""{"version":3,"file":"custom.js","sourceRoot":"","sources":["custom.ts"],"names":[],"mappings":"AAEA,SAAS,CAAC,CAAC,CAAM;IAChB,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED,IAAI,CAAC,GAAG,UAAU,CAAM;IACvB,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AACb,CAAC,CAAA;AAED,CAAC,CAAC,CAAC,CAAC,CAAC"}""");
  46. string BuildCallStackHandler(string description, SourceLocation location, string[] arguments)
  47. {
  48. if (location.SourceFile != sourceMap.File)
  49. {
  50. return null;
  51. }
  52. var originalPosition = sourceMap.OriginalPositionFor(location.End.Line, location.Start.Column + 1);
  53. if (originalPosition is null)
  54. {
  55. return null;
  56. }
  57. var str = $" at {
  58. (!string.IsNullOrWhiteSpace(description) ? $"{description} (" : "")
  59. }{
  60. originalPosition.Value.OriginalFileName
  61. }:{
  62. originalPosition.Value.OriginalLineNumber + 1
  63. }:{
  64. originalPosition.Value.OriginalColumnNumber
  65. }{
  66. (!string.IsNullOrWhiteSpace(description) ? ")" : "")
  67. }{
  68. Environment.NewLine
  69. }";
  70. return str;
  71. }
  72. var engine = new Engine(opt =>
  73. {
  74. opt.SetBuildCallStackHandler(BuildCallStackHandler);
  75. });
  76. const string Script = @"function a(v) {
  77. throw new Error(v);
  78. }
  79. var b = function (v) {
  80. return a(v);
  81. };
  82. b(7);
  83. //# sourceMappingURL=custom.js.map";
  84. var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(Script, "custom.js"));
  85. var stack = ex.JavaScriptStackTrace!;
  86. Assert.Equal(@" at a (custom.ts:4:7)
  87. at b (custom.ts:8:9)
  88. at custom.ts:11:1".Replace("\r\n", "\n"), stack.Replace("\r\n", "\n"));
  89. }
  90. }