2
0

ErrorTests.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. using Esprima;
  2. using Jint.Runtime;
  3. using System;
  4. using System.Collections.Generic;
  5. using Xunit;
  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 CanProduceCorrectStackTrace()
  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. ", new ParserOptions("custom.js"));
  61. var e = Assert.Throws<JavaScriptException>(() => engine.Execute("var x = b(7);", new ParserOptions("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.StackTrace;
  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 ErrorObjectHasTheStackTraceImmediately()
  73. {
  74. var engine = new Engine();
  75. engine.Execute(@"
  76. var a = function(v) {
  77. return Error().stack;
  78. }
  79. var b = function(v) {
  80. return a(v);
  81. }
  82. ", new ParserOptions("custom.js"));
  83. var e = engine.Evaluate(@"b(7)", new ParserOptions("main.js")).AsString();
  84. var stack = e;
  85. EqualIgnoringNewLineDifferences(@" at Error custom.js:3:10
  86. at a (v) custom.js:3:10
  87. at b (v) custom.js:7:10
  88. at main.js:1:1", stack);
  89. }
  90. [Fact]
  91. public void ThrownErrorObjectHasStackTraceInCatch()
  92. {
  93. var engine = new Engine();
  94. engine.Execute(@"
  95. var a = function(v) {
  96. try {
  97. throw Error();
  98. } catch(err) {
  99. return err.stack;
  100. }
  101. }
  102. var b = function(v) {
  103. return a(v);
  104. }
  105. ", new ParserOptions("custom.js"));
  106. var e = engine.Evaluate(@"b(7)", new ParserOptions("main.js")).AsString();
  107. var stack = e;
  108. EqualIgnoringNewLineDifferences(@" at Error custom.js:4:11
  109. at a (v) custom.js:4:11
  110. at b (v) custom.js:11:10
  111. at main.js:1:1", stack);
  112. }
  113. [Fact]
  114. public void GeneratedErrorHasStackTraceInCatch()
  115. {
  116. var engine = new Engine();
  117. engine.Execute(@"
  118. var a = function(v) {
  119. try {
  120. var a = ''.xyz();
  121. } catch(err) {
  122. return err.stack;
  123. }
  124. }
  125. var b = function(v) {
  126. return a(v);
  127. }
  128. ", new ParserOptions("custom.js"));
  129. var e = engine.Evaluate(@"b(7)", new ParserOptions("main.js")).AsString();
  130. var stack = e;
  131. EqualIgnoringNewLineDifferences(@" at a (v) custom.js:4:13
  132. at b (v) custom.js:11:10
  133. at main.js:1:1", stack);
  134. }
  135. [Fact]
  136. public void ErrorObjectHasOwnPropertyStack()
  137. {
  138. var res = new Engine().Evaluate(@"Error().hasOwnProperty('stack')").AsBoolean();
  139. Assert.True(res);
  140. }
  141. private class Folder
  142. {
  143. public Folder Parent { get; set; }
  144. public string Name { get; set; }
  145. }
  146. [Fact]
  147. public void CallStackBuildingShouldSkipResolvingFromEngine()
  148. {
  149. var engine = new Engine(o => o.LimitRecursion(200));
  150. var recordedFolderTraversalOrder = new List<string>();
  151. engine.SetValue("log", new Action<object>(o => recordedFolderTraversalOrder.Add(o.ToString())));
  152. var folder = new Folder
  153. {
  154. Name = "SubFolder2",
  155. Parent = new Folder
  156. {
  157. Name = "SubFolder1",
  158. Parent = new Folder
  159. {
  160. Name = "Root",
  161. Parent = null,
  162. }
  163. }
  164. };
  165. engine.SetValue("folder", folder);
  166. var javaScriptException = Assert.Throws<JavaScriptException>(() =>
  167. engine.Execute(@"
  168. var Test = {
  169. recursive: function(folderInstance) {
  170. // Enabling the guard here corrects the problem, but hides the hard fault
  171. // if (folderInstance==null) return null;
  172. log(folderInstance.Name);
  173. if (folderInstance==null) return null;
  174. return this.recursive(folderInstance.parent);
  175. }
  176. }
  177. Test.recursive(folder);"
  178. ));
  179. Assert.Equal("Cannot read property 'Name' of null", javaScriptException.Message);
  180. EqualIgnoringNewLineDifferences(@" at recursive (folderInstance) <anonymous>:6:44
  181. at recursive (folderInstance) <anonymous>:8:32
  182. at recursive (folderInstance) <anonymous>:8:32
  183. at recursive (folderInstance) <anonymous>:8:32
  184. at <anonymous>:12:17", javaScriptException.StackTrace);
  185. var expected = new List<string>
  186. {
  187. "SubFolder2", "SubFolder1", "Root"
  188. };
  189. Assert.Equal(expected, recordedFolderTraversalOrder);
  190. }
  191. [Fact]
  192. public void StackTraceCollectedOnThreeLevels()
  193. {
  194. var engine = new Engine();
  195. const string script = @"var a = function(v) {
  196. return v.xxx.yyy;
  197. }
  198. var b = function(v) {
  199. return a(v);
  200. }
  201. var x = b(7);";
  202. var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
  203. const string expected = @"Jint.Runtime.JavaScriptException: Cannot read property 'yyy' of undefined
  204. at a (v) <anonymous>:2:18
  205. at b (v) <anonymous>:6:12
  206. at <anonymous>:9:9";
  207. EqualIgnoringNewLineDifferences(expected, ex.ToString());
  208. }
  209. [Fact]
  210. public void StackTraceCollectedForImmediatelyInvokedFunctionExpression()
  211. {
  212. var engine = new Engine();
  213. const string script = @"function getItem(items, itemIndex) {
  214. var item = items[itemIndex];
  215. return item;
  216. }
  217. (function (getItem) {
  218. var items = null,
  219. item = getItem(items, 5)
  220. ;
  221. return item;
  222. })(getItem);";
  223. var parserOptions = new ParserOptions("get-item.js")
  224. {
  225. AdaptRegexp = true,
  226. Tolerant = true
  227. };
  228. var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(script, parserOptions));
  229. const string expected = @"Jint.Runtime.JavaScriptException: Cannot read property '5' of null
  230. at getItem (items, itemIndex) get-item.js:2:22
  231. at (anonymous) (getItem) get-item.js:9:16
  232. at get-item.js:13:2";
  233. EqualIgnoringNewLineDifferences(expected, ex.ToString());
  234. }
  235. [Theory]
  236. [InlineData("Error")]
  237. [InlineData("EvalError")]
  238. [InlineData("RangeError")]
  239. [InlineData("SyntaxError")]
  240. [InlineData("TypeError")]
  241. [InlineData("ReferenceError")]
  242. public void ErrorsHaveCorrectConstructor(string type)
  243. {
  244. var engine = new Engine();
  245. engine.Execute($"const o = new {type}();");
  246. Assert.True(engine.Evaluate($"o.constructor === {type}").AsBoolean());
  247. Assert.Equal(type, engine.Evaluate("o.constructor.name").AsString());
  248. }
  249. private static void EqualIgnoringNewLineDifferences(string expected, string actual)
  250. {
  251. expected = expected.Replace("\r\n", "\n");
  252. actual = actual.Replace("\r\n", "\n");
  253. Assert.Equal(expected, actual);
  254. }
  255. }
  256. }