ErrorTests.cs 15 KB

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