CallStackTests.cs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  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. Environment.NewLine
  67. }";
  68. return str;
  69. }
  70. var engine = new Engine(opt =>
  71. {
  72. opt.SetBuildCallStackHandler(BuildCallStackHandler);
  73. });
  74. const string Script = @"function a(v) {
  75. throw new Error(v);
  76. }
  77. var b = function (v) {
  78. return a(v);
  79. };
  80. b(7);
  81. //# sourceMappingURL=custom.js.map";
  82. var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(Script, "custom.js"));
  83. var stack = ex.JavaScriptStackTrace!;
  84. Assert.Equal(@" at a custom.ts:4:7
  85. at b custom.ts:8:9
  86. at custom.ts:11:1".Replace("\r\n", "\n"), stack.Replace("\r\n", "\n"));
  87. }
  88. }