ErrorTests.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. using Esprima;
  2. using Jint.Runtime;
  3. using Jint.Tests.Runtime.TestClasses;
  4. using System;
  5. using System.Collections.Generic;
  6. using Xunit;
  7. namespace Jint.Tests.Runtime
  8. {
  9. public class ErrorTests
  10. {
  11. [Fact]
  12. public void CanReturnCorrectErrorMessageAndLocation1()
  13. {
  14. const string script = @"
  15. var a = {};
  16. var b = a.user.name;
  17. ";
  18. var engine = new Engine();
  19. var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
  20. Assert.Equal("Cannot read property 'name' of undefined", e.Message);
  21. Assert.Equal(4, e.Location.Start.Line);
  22. Assert.Equal(15, e.Location.Start.Column);
  23. }
  24. [Fact]
  25. public void CanReturnCorrectErrorMessageAndLocation1WithoutReferencedName()
  26. {
  27. const string script = @"
  28. var c = a(b().Length);
  29. ";
  30. var engine = new Engine();
  31. engine.SetValue("a", new Action<string>((_) => { }));
  32. engine.SetValue("b", new Func<string>(() => null));
  33. var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
  34. Assert.Equal("Cannot read property 'Length' of null", e.Message);
  35. Assert.Equal(2, e.Location.Start.Line);
  36. Assert.Equal(14, e.Location.Start.Column);
  37. }
  38. [Fact]
  39. public void CanReturnCorrectErrorMessageAndLocation2()
  40. {
  41. const string script = @"
  42. test();
  43. ";
  44. var engine = new Engine();
  45. var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
  46. Assert.Equal("test is not defined", e.Message);
  47. Assert.Equal(2, e.Location.Start.Line);
  48. Assert.Equal(1, e.Location.Start.Column);
  49. }
  50. [Fact]
  51. public void CanProduceCorrectStackTrace()
  52. {
  53. var engine = new Engine();
  54. engine.Execute(@"
  55. var a = function(v) {
  56. return v.xxx.yyy;
  57. }
  58. var b = function(v) {
  59. return a(v);
  60. }
  61. ", new ParserOptions("custom.js"));
  62. var e = Assert.Throws<JavaScriptException>(() => engine.Execute("var x = b(7);", new ParserOptions("main.js")));
  63. Assert.Equal("Cannot read property 'yyy' of undefined", e.Message);
  64. Assert.Equal(3, e.Location.Start.Line);
  65. Assert.Equal(15, e.Location.Start.Column);
  66. Assert.Equal("custom.js", e.Location.Source);
  67. var stack = e.StackTrace;
  68. EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:16
  69. at b (v) custom.js:7:10
  70. at main.js:1:9", stack);
  71. }
  72. [Fact]
  73. public void ErrorObjectHasTheStackTraceImmediately()
  74. {
  75. var engine = new Engine();
  76. engine.Execute(@"
  77. var a = function(v) {
  78. return Error().stack;
  79. }
  80. var b = function(v) {
  81. return a(v);
  82. }
  83. ", new ParserOptions("custom.js"));
  84. var e = engine.Evaluate(@"b(7)", new ParserOptions("main.js")).AsString();
  85. var stack = e;
  86. EqualIgnoringNewLineDifferences(@" at Error custom.js:3:10
  87. at a (v) custom.js:3:10
  88. at b (v) custom.js:7:10
  89. at main.js:1:1", stack);
  90. }
  91. [Fact]
  92. public void ThrownErrorObjectHasStackTraceInCatch()
  93. {
  94. var engine = new Engine();
  95. engine.Execute(@"
  96. var a = function(v) {
  97. try {
  98. throw Error();
  99. } catch(err) {
  100. return err.stack;
  101. }
  102. }
  103. var b = function(v) {
  104. return a(v);
  105. }
  106. ", new ParserOptions("custom.js"));
  107. var e = engine.Evaluate(@"b(7)", new ParserOptions("main.js")).AsString();
  108. var stack = e;
  109. EqualIgnoringNewLineDifferences(@" at Error custom.js:4:11
  110. at a (v) custom.js:4:11
  111. at b (v) custom.js:11:10
  112. at main.js:1:1", stack);
  113. }
  114. [Fact]
  115. public void GeneratedErrorHasStackTraceInCatch()
  116. {
  117. var engine = new Engine();
  118. engine.Execute(@"
  119. var a = function(v) {
  120. try {
  121. var a = ''.xyz();
  122. } catch(err) {
  123. return err.stack;
  124. }
  125. }
  126. var b = function(v) {
  127. return a(v);
  128. }
  129. ", new ParserOptions("custom.js"));
  130. var e = engine.Evaluate(@"b(7)", new ParserOptions("main.js")).AsString();
  131. var stack = e;
  132. EqualIgnoringNewLineDifferences(@" at a (v) custom.js:4:13
  133. at b (v) custom.js:11:10
  134. at main.js:1:1", stack);
  135. }
  136. [Fact]
  137. public void ErrorObjectHasOwnPropertyStack()
  138. {
  139. var res = new Engine().Evaluate(@"Error().hasOwnProperty('stack')").AsBoolean();
  140. Assert.True(res);
  141. }
  142. private class Folder
  143. {
  144. public Folder Parent { get; set; }
  145. public string Name { get; set; }
  146. }
  147. [Fact]
  148. public void CallStackBuildingShouldSkipResolvingFromEngine()
  149. {
  150. var engine = new Engine(o => o.LimitRecursion(200));
  151. var recordedFolderTraversalOrder = new List<string>();
  152. engine.SetValue("log", new Action<object>(o => recordedFolderTraversalOrder.Add(o.ToString())));
  153. var folder = new Folder
  154. {
  155. Name = "SubFolder2",
  156. Parent = new Folder
  157. {
  158. Name = "SubFolder1",
  159. Parent = new Folder
  160. {
  161. Name = "Root",
  162. Parent = null,
  163. }
  164. }
  165. };
  166. engine.SetValue("folder", folder);
  167. var javaScriptException = Assert.Throws<JavaScriptException>(() =>
  168. engine.Execute(@"
  169. var Test = {
  170. recursive: function(folderInstance) {
  171. // Enabling the guard here corrects the problem, but hides the hard fault
  172. // if (folderInstance==null) return null;
  173. log(folderInstance.Name);
  174. if (folderInstance==null) return null;
  175. return this.recursive(folderInstance.parent);
  176. }
  177. }
  178. Test.recursive(folder);"
  179. ));
  180. Assert.Equal("Cannot read property 'Name' of null", javaScriptException.Message);
  181. EqualIgnoringNewLineDifferences(@" at recursive (folderInstance) <anonymous>:6:44
  182. at recursive (folderInstance) <anonymous>:8:32
  183. at recursive (folderInstance) <anonymous>:8:32
  184. at recursive (folderInstance) <anonymous>:8:32
  185. at <anonymous>:12:17", javaScriptException.StackTrace);
  186. var expected = new List<string>
  187. {
  188. "SubFolder2", "SubFolder1", "Root"
  189. };
  190. Assert.Equal(expected, recordedFolderTraversalOrder);
  191. }
  192. [Fact]
  193. public void StackTraceCollectedOnThreeLevels()
  194. {
  195. var engine = new Engine();
  196. const string script = @"var a = function(v) {
  197. return v.xxx.yyy;
  198. }
  199. var b = function(v) {
  200. return a(v);
  201. }
  202. var x = b(7);";
  203. var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
  204. const string expected = @"Jint.Runtime.JavaScriptException: Cannot read property 'yyy' of undefined
  205. at a (v) <anonymous>:2:18
  206. at b (v) <anonymous>:6:12
  207. at <anonymous>:9:9";
  208. EqualIgnoringNewLineDifferences(expected, ex.ToString());
  209. }
  210. [Fact]
  211. public void StackTraceCollectedForImmediatelyInvokedFunctionExpression()
  212. {
  213. var engine = new Engine();
  214. const string script = @"function getItem(items, itemIndex) {
  215. var item = items[itemIndex];
  216. return item;
  217. }
  218. (function (getItem) {
  219. var items = null,
  220. item = getItem(items, 5)
  221. ;
  222. return item;
  223. })(getItem);";
  224. var parserOptions = new ParserOptions("get-item.js")
  225. {
  226. AdaptRegexp = true,
  227. Tolerant = true
  228. };
  229. var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(script, parserOptions));
  230. const string expected = @"Jint.Runtime.JavaScriptException: Cannot read property '5' of null
  231. at getItem (items, itemIndex) get-item.js:2:22
  232. at (anonymous) (getItem) get-item.js:9:16
  233. at get-item.js:13:2";
  234. EqualIgnoringNewLineDifferences(expected, ex.ToString());
  235. }
  236. [Fact]
  237. public void StackTraceIsForOriginalException()
  238. {
  239. var engine = new Engine();
  240. engine.SetValue("HelloWorld", new HelloWorld());
  241. const string script = @"HelloWorld.ThrowException();";
  242. var ex = Assert.Throws<DivideByZeroException>(() => engine.Execute(script));
  243. const string expected = "HelloWorld";
  244. ContainsIgnoringNewLineDifferences(expected, ex.ToString());
  245. }
  246. [Theory]
  247. [InlineData("Error")]
  248. [InlineData("EvalError")]
  249. [InlineData("RangeError")]
  250. [InlineData("SyntaxError")]
  251. [InlineData("TypeError")]
  252. [InlineData("ReferenceError")]
  253. public void ErrorsHaveCorrectConstructor(string type)
  254. {
  255. var engine = new Engine();
  256. engine.Execute($"const o = new {type}();");
  257. Assert.True(engine.Evaluate($"o.constructor === {type}").AsBoolean());
  258. Assert.Equal(type, engine.Evaluate("o.constructor.name").AsString());
  259. }
  260. private static void EqualIgnoringNewLineDifferences(string expected, string actual)
  261. {
  262. expected = expected.Replace("\r\n", "\n");
  263. actual = actual.Replace("\r\n", "\n");
  264. Assert.Equal(expected, actual);
  265. }
  266. private static void ContainsIgnoringNewLineDifferences(string expectedSubstring, string actualString)
  267. {
  268. expectedSubstring = expectedSubstring.Replace("\r\n", "\n");
  269. actualString = actualString.Replace("\r\n", "\n");
  270. Assert.Contains(expectedSubstring, actualString);
  271. }
  272. [Fact]
  273. public void CustomException()
  274. {
  275. var engine = new Engine();
  276. const string filename = "someFile.js";
  277. JintJsException jsException = Assert.Throws<JintJsException>(() =>
  278. {
  279. try
  280. {
  281. const string script = @"
  282. var test = 42; // just adding a line for a non zero line offset
  283. throw new Error('blah');
  284. ";
  285. engine.Execute(script);
  286. }
  287. catch (JavaScriptException ex)
  288. {
  289. throw new JintJsException(filename, ex);
  290. }
  291. });
  292. Assert.Equal(24, jsException.Column);
  293. Assert.Equal(3, jsException.LineNumber);
  294. Assert.Equal(filename, jsException.Module);
  295. }
  296. [Fact]
  297. public void CustomExceptionUsesCopyConstructor()
  298. {
  299. var engine = new Engine();
  300. const string filename = "someFile.js";
  301. JintJsException2 jsException = Assert.Throws<JintJsException2>(() =>
  302. {
  303. try
  304. {
  305. const string script = @"
  306. var test = 42; // just adding a line for a non zero line offset
  307. throw new Error('blah');
  308. ";
  309. engine.Execute(script);
  310. }
  311. catch (JavaScriptException ex)
  312. {
  313. throw new JintJsException2(filename, ex);
  314. }
  315. });
  316. Assert.Equal(24, jsException.Column);
  317. Assert.Equal(3, jsException.LineNumber);
  318. Assert.Equal(filename, jsException.Module);
  319. }
  320. }
  321. public class JintJsException : JavaScriptException
  322. {
  323. private readonly JavaScriptException _jsException;
  324. public JintJsException(string moduleName, JavaScriptException jsException) : base(jsException.Error)
  325. {
  326. Module = moduleName;
  327. _jsException = jsException;
  328. Location = jsException.Location;
  329. }
  330. public string Module { get; }
  331. public override string Message
  332. {
  333. get
  334. {
  335. var scriptFilename = (Module != null) ? "Filepath: " + Module + " " : "";
  336. var errorMsg = $"{scriptFilename}{_jsException.Message}";
  337. return errorMsg;
  338. }
  339. }
  340. public override string StackTrace => _jsException.StackTrace;
  341. }
  342. public class JintJsException2 : JavaScriptException
  343. {
  344. public JintJsException2(string moduleName, JavaScriptException jsException) : base(jsException)
  345. {
  346. Module = moduleName;
  347. }
  348. public string Module { get; }
  349. }
  350. }