ErrorTests.cs 15 KB

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