ErrorTests.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. using System.Reflection;
  2. using Jint.Native;
  3. using Jint.Runtime;
  4. using Jint.Tests.Runtime.TestClasses;
  5. namespace Jint.Tests.Runtime;
  6. public class ErrorTests
  7. {
  8. [Fact]
  9. public void CanReturnCorrectErrorMessageAndLocation1()
  10. {
  11. const string script = @"
  12. var a = {};
  13. var b = a.user.name;
  14. ";
  15. var engine = new Engine();
  16. var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
  17. Assert.Equal("Cannot read property 'name' of undefined", e.Message);
  18. Assert.Equal(4, e.Location.Start.Line);
  19. Assert.Equal(15, e.Location.Start.Column);
  20. }
  21. [Fact]
  22. public void CanReturnCorrectErrorMessageAndLocation1WithoutReferencedName()
  23. {
  24. const string script = @"
  25. var c = a(b().Length);
  26. ";
  27. var engine = new Engine();
  28. engine.SetValue("a", new Action<string>((_) => { }));
  29. engine.SetValue("b", new Func<string>(() => null));
  30. var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
  31. Assert.Equal("Cannot read property 'Length' of null", e.Message);
  32. Assert.Equal(2, e.Location.Start.Line);
  33. Assert.Equal(14, e.Location.Start.Column);
  34. }
  35. [Fact]
  36. public void CanReturnCorrectErrorMessageAndLocation2()
  37. {
  38. const string script = @"
  39. test();
  40. ";
  41. var engine = new Engine();
  42. var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
  43. Assert.Equal("test is not defined", e.Message);
  44. Assert.Equal(2, e.Location.Start.Line);
  45. Assert.Equal(1, e.Location.Start.Column);
  46. }
  47. [Fact]
  48. public void CanProduceCorrectStackTraceForInternalError()
  49. {
  50. var engine = new Engine();
  51. engine.Execute(@"
  52. var a = function(v) {
  53. return v.xxx.yyy;
  54. }
  55. var b = function(v) {
  56. return a(v);
  57. }
  58. ", "custom.js");
  59. var e = Assert.Throws<JavaScriptException>(() => engine.Execute("var x = b(7);", "main.js"));
  60. Assert.Equal("Cannot read property 'yyy' of undefined", e.Message);
  61. Assert.Equal(3, e.Location.Start.Line);
  62. Assert.Equal(15, e.Location.Start.Column);
  63. Assert.Equal("custom.js", e.Location.SourceFile);
  64. var stack = e.JavaScriptStackTrace;
  65. EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:16
  66. at b (v) custom.js:7:10
  67. at main.js:1:9", stack);
  68. }
  69. [Fact]
  70. public void CanProduceCorrectStackTraceForScriptError()
  71. {
  72. var engine = new Engine();
  73. engine.Execute(@"
  74. var a = function(v) {
  75. throw new Error('Error thrown from script');
  76. }
  77. var b = function(v) {
  78. return a(v);
  79. }
  80. ", "custom.js");
  81. var e = Assert.Throws<JavaScriptException>(() => engine.Execute("var x = b(7);", "main.js"));
  82. Assert.Equal("Error thrown from script", e.Message);
  83. Assert.Equal(3, e.Location.Start.Line);
  84. Assert.Equal(8, e.Location.Start.Column);
  85. Assert.Equal("custom.js", e.Location.SourceFile);
  86. var stack = e.JavaScriptStackTrace;
  87. EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:9
  88. at b (v) custom.js:7:10
  89. at main.js:1:9", stack);
  90. }
  91. [Fact]
  92. public void ErrorObjectHasTheStackTraceImmediately()
  93. {
  94. var engine = new Engine();
  95. engine.Execute(@"
  96. var a = function(v) {
  97. return Error().stack;
  98. }
  99. var b = function(v) {
  100. return a(v);
  101. }
  102. ", "custom.js");
  103. var e = engine.Evaluate(@"b(7)", "main.js").AsString();
  104. var stack = e;
  105. EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:10
  106. at b (v) custom.js:7:10
  107. at main.js:1:1", stack);
  108. }
  109. [Fact]
  110. public void ThrownErrorObjectHasStackTraceInCatch()
  111. {
  112. var engine = new Engine();
  113. engine.Execute(@"
  114. var a = function(v) {
  115. try {
  116. throw Error();
  117. } catch(err) {
  118. return err.stack;
  119. }
  120. }
  121. var b = function(v) {
  122. return a(v);
  123. }
  124. ", "custom.js");
  125. var e = engine.Evaluate(@"b(7)", "main.js").AsString();
  126. var stack = e;
  127. EqualIgnoringNewLineDifferences(@" at a (v) custom.js:4:11
  128. at b (v) custom.js:11:10
  129. at main.js:1:1", stack);
  130. }
  131. [Fact]
  132. public void GeneratedErrorHasStackTraceInCatch()
  133. {
  134. var engine = new Engine();
  135. engine.Execute(@"
  136. var a = function(v) {
  137. try {
  138. var a = ''.xyz();
  139. } catch(err) {
  140. return err.stack;
  141. }
  142. }
  143. var b = function(v) {
  144. return a(v);
  145. }
  146. ", "custom.js");
  147. var e = engine.Evaluate(@"b(7)", "main.js").AsString();
  148. var stack = e;
  149. EqualIgnoringNewLineDifferences(@" at a (v) custom.js:4:13
  150. at b (v) custom.js:11:10
  151. at main.js:1:1", stack);
  152. }
  153. [Fact]
  154. public void ErrorObjectHasOwnPropertyStack()
  155. {
  156. var res = new Engine().Evaluate(@"Error().hasOwnProperty('stack')").AsBoolean();
  157. Assert.True(res);
  158. }
  159. private class Folder
  160. {
  161. public Folder Parent { get; set; }
  162. public string Name { get; set; }
  163. }
  164. [Fact]
  165. public void CallStackBuildingShouldSkipResolvingFromEngine()
  166. {
  167. var engine = new Engine(o => o.LimitRecursion(200));
  168. var recordedFolderTraversalOrder = new List<string>();
  169. engine.SetValue("log", new Action<object>(o => recordedFolderTraversalOrder.Add(o.ToString())));
  170. var folder = new Folder
  171. {
  172. Name = "SubFolder2",
  173. Parent = new Folder
  174. {
  175. Name = "SubFolder1",
  176. Parent = new Folder
  177. {
  178. Name = "Root",
  179. Parent = null,
  180. }
  181. }
  182. };
  183. engine.SetValue("folder", folder);
  184. var javaScriptException = Assert.Throws<JavaScriptException>(() =>
  185. engine.Execute(@"
  186. var Test = {
  187. recursive: function(folderInstance) {
  188. // Enabling the guard here corrects the problem, but hides the hard fault
  189. // if (folderInstance==null) return null;
  190. log(folderInstance.Name);
  191. if (folderInstance==null) return null;
  192. return this.recursive(folderInstance.parent);
  193. }
  194. }
  195. Test.recursive(folder);"
  196. ));
  197. Assert.Equal("Cannot read property 'Name' of null", javaScriptException.Message);
  198. EqualIgnoringNewLineDifferences(@" at recursive (folderInstance) <anonymous>:6:44
  199. at recursive (folderInstance) <anonymous>:8:32
  200. at recursive (folderInstance) <anonymous>:8:32
  201. at recursive (folderInstance) <anonymous>:8:32
  202. at <anonymous>:12:17", javaScriptException.JavaScriptStackTrace);
  203. var expected = new List<string>
  204. {
  205. "SubFolder2", "SubFolder1", "Root"
  206. };
  207. Assert.Equal(expected, recordedFolderTraversalOrder);
  208. }
  209. [Fact]
  210. public void StackTraceCollectedOnThreeLevels()
  211. {
  212. var engine = new Engine();
  213. const string script = @"var a = function(v) {
  214. return v.xxx.yyy;
  215. }
  216. var b = function(v) {
  217. return a(v);
  218. }
  219. var x = b(7);";
  220. var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
  221. const string expected = @"Error: Cannot read property 'yyy' of undefined
  222. at a (v) <anonymous>:2:18
  223. at b (v) <anonymous>:6:12
  224. at <anonymous>:9:9";
  225. EqualIgnoringNewLineDifferences(expected, ex.GetJavaScriptErrorString());
  226. Assert.Equal(2, ex.Location.Start.Line);
  227. Assert.Equal(17, ex.Location.Start.Column);
  228. }
  229. [Fact]
  230. public void StackTraceCollectedForImmediatelyInvokedFunctionExpression()
  231. {
  232. var engine = new Engine();
  233. const string script = @"function getItem(items, itemIndex) {
  234. var item = items[itemIndex];
  235. return item;
  236. }
  237. (function (getItem) {
  238. var items = null,
  239. item = getItem(items, 5)
  240. ;
  241. return item;
  242. })(getItem);";
  243. var parsingOptions = new ScriptParsingOptions
  244. {
  245. CompileRegex = false,
  246. Tolerant = true
  247. };
  248. var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(script, "get-item.js", parsingOptions));
  249. const string expected = @"Error: Cannot read property '5' of null
  250. at getItem (items, itemIndex) get-item.js:2:22
  251. at (anonymous) (getItem) get-item.js:9:16
  252. at get-item.js:13:2";
  253. EqualIgnoringNewLineDifferences(expected, ex.GetJavaScriptErrorString());
  254. Assert.Equal(2, ex.Location.Start.Line);
  255. Assert.Equal(21, ex.Location.Start.Column);
  256. }
  257. // Verify #1202
  258. [Fact]
  259. public void StackIsUnwoundWhenExceptionHandledByInteropCode()
  260. {
  261. var engine = new Engine()
  262. .SetValue("handle", new Action<Action>(Handler));
  263. const string Script = @"
  264. function throwIt(message) {
  265. throw new Error(message);
  266. }
  267. handle(() => throwIt('e1'));
  268. handle(() => throwIt('e2'));
  269. handle(() => throwIt('e3'));
  270. try {
  271. throwIt('e4');
  272. } catch(x){
  273. x.stack; // return stack trace string
  274. }
  275. ";
  276. var stack = engine.Evaluate(Script).AsString();
  277. EqualIgnoringNewLineDifferences(@" at throwIt (message) <anonymous>:3:11
  278. at <anonymous>:11:5", stack);
  279. static void Handler(Action callback)
  280. {
  281. try
  282. {
  283. callback();
  284. }
  285. catch (JavaScriptException)
  286. {
  287. // handle JS error
  288. }
  289. }
  290. }
  291. [Fact]
  292. public void StackTraceIsForOriginalException()
  293. {
  294. var engine = new Engine();
  295. engine.SetValue("HelloWorld", new HelloWorld());
  296. const string script = @"HelloWorld.ThrowException();";
  297. var ex = Assert.Throws<DivideByZeroException>(() => engine.Execute(script));
  298. const string expected = "HelloWorld";
  299. ContainsIgnoringNewLineDifferences(expected, ex.ToString());
  300. }
  301. [Theory]
  302. [InlineData("Error")]
  303. [InlineData("EvalError")]
  304. [InlineData("RangeError")]
  305. [InlineData("SyntaxError")]
  306. [InlineData("TypeError")]
  307. [InlineData("ReferenceError")]
  308. public void ErrorsHaveCorrectConstructor(string type)
  309. {
  310. var engine = new Engine();
  311. engine.Execute($"const o = new {type}();");
  312. Assert.True(engine.Evaluate($"o.constructor === {type}").AsBoolean());
  313. Assert.Equal(type, engine.Evaluate("o.constructor.name").AsString());
  314. }
  315. [Fact]
  316. public void CallStackWorksWithRecursiveCalls()
  317. {
  318. static ScriptParsingOptions CreateParsingOptions()
  319. {
  320. return new ScriptParsingOptions
  321. {
  322. CompileRegex = false,
  323. Tolerant = true
  324. };
  325. }
  326. var e = Assert.Throws<JavaScriptException>(() =>
  327. {
  328. var engine = new Engine();
  329. engine.SetValue("executeFile", (Action<string>) (path =>
  330. {
  331. var content = path switch
  332. {
  333. "first-file.js" => @"num = num * 3;
  334. executeFile(""second-file.js"");",
  335. "second-file.js" => @"// Intentionally making a mistake in the variable name
  336. nuм -= 3;",
  337. _ => throw new FileNotFoundException($"File '{path}' not exist.", path)
  338. };
  339. engine.Execute(content, path, CreateParsingOptions());
  340. }));
  341. engine.Execute(
  342. @"var num = 5;
  343. executeFile(""first-file.js"");",
  344. "main-file.js",
  345. CreateParsingOptions()
  346. );
  347. });
  348. Assert.Equal("nuм is not defined", e.Message);
  349. const string Expected = @" at delegate second-file.js:2:1
  350. at delegate first-file.js:2:1
  351. at main-file.js:2:1";
  352. EqualIgnoringNewLineDifferences(Expected, e.JavaScriptStackTrace);
  353. }
  354. [Fact]
  355. public void ShouldReportCorrectColumn()
  356. {
  357. var e = Assert.Throws<JavaScriptException>(() =>
  358. {
  359. var engine = new Engine();
  360. engine.Execute(@"var $variable1 = 611;
  361. var _variable2 = 711;
  362. var variable3 = 678;
  363. $variable1 + -variable2 - variable3;");
  364. });
  365. Assert.Equal(5, e.Location.Start.Line);
  366. Assert.Equal(14, e.Location.Start.Column);
  367. Assert.Equal(" at <anonymous>:5:15", e.JavaScriptStackTrace);
  368. }
  369. [Fact]
  370. public void InvokingDelegateShouldContainJavascriptExceptionAsInnerException()
  371. {
  372. Delegate func = null;
  373. void SetFuncValue(Delegate scriptFunc) => func = scriptFunc;
  374. var engine = new Engine();
  375. engine.SetValue("SetFuncValue", SetFuncValue);
  376. engine.Execute("SetFuncValue(() => { foo.bar });");
  377. var ex = Assert.Throws<TargetInvocationException>(() => func?.DynamicInvoke(JsValue.Undefined, Array.Empty<JsValue>()));
  378. var exception = Assert.IsType<JavaScriptException>(ex.InnerException);
  379. Assert.Equal("foo is not defined", exception.Message);
  380. }
  381. [Fact]
  382. public void JavaScriptExceptionLocationOnModuleShouldBeRight()
  383. {
  384. var engine = new Engine();
  385. engine.Modules.Add("my_module", @"
  386. function throw_error(){
  387. throw Error(""custom error"")
  388. }
  389. throw_error();
  390. ");
  391. var ex= Assert.Throws<JavaScriptException>(() => engine.Modules.Import("my_module"));
  392. Assert.Equal(ex.Location.Start.Line, 3);
  393. Assert.Equal(ex.Location.Start.Column, 10);
  394. }
  395. private static void EqualIgnoringNewLineDifferences(string expected, string actual)
  396. {
  397. expected = expected.Replace("\r\n", "\n");
  398. actual = actual.Replace("\r\n", "\n");
  399. Assert.Equal(expected, actual);
  400. }
  401. private static void ContainsIgnoringNewLineDifferences(string expectedSubstring, string actualString)
  402. {
  403. expectedSubstring = expectedSubstring.Replace("\r\n", "\n");
  404. actualString = actualString.Replace("\r\n", "\n");
  405. Assert.Contains(expectedSubstring, actualString);
  406. }
  407. }