ScopeTests.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. using Jint.Native;
  2. using Jint.Runtime.Debugger;
  3. using System.Linq;
  4. using Xunit;
  5. namespace Jint.Tests.Runtime.Debugger
  6. {
  7. public class ScopeTests
  8. {
  9. private static JsValue AssertOnlyScopeContains(DebugScopes scopes, string name, DebugScopeType scopeType)
  10. {
  11. var containingScope = Assert.Single(scopes, s => s.ScopeType == scopeType && s.BindingNames.Contains(name));
  12. Assert.DoesNotContain(scopes, s => s != containingScope && s.BindingNames.Contains(name));
  13. return containingScope.GetBindingValue(name);
  14. }
  15. private static void AssertScope(DebugScope actual, DebugScopeType expectedType, params string[] expectedBindingNames)
  16. {
  17. Assert.Equal(expectedType, actual.ScopeType);
  18. // Global scope will have a number of intrinsic bindings that are outside the scope [no pun] of these tests
  19. if (actual.ScopeType != DebugScopeType.Global)
  20. {
  21. Assert.Equal(expectedBindingNames.Length, actual.BindingNames.Count);
  22. }
  23. foreach (string expectedName in expectedBindingNames)
  24. {
  25. Assert.Contains(expectedName, actual.BindingNames);
  26. }
  27. }
  28. [Fact]
  29. public void GlobalScopeIncludesGlobalConst()
  30. {
  31. string script = @"
  32. const globalConstant = 'test';
  33. debugger;
  34. ";
  35. TestHelpers.TestAtBreak(script, info =>
  36. {
  37. var value = AssertOnlyScopeContains(info.CurrentScopeChain, "globalConstant", DebugScopeType.Global);
  38. Assert.Equal("test", value.AsString());
  39. });
  40. }
  41. [Fact]
  42. public void GlobalScopeIncludesGlobalLet()
  43. {
  44. string script = @"
  45. let globalLet = 'test';
  46. debugger;";
  47. TestHelpers.TestAtBreak(script, info =>
  48. {
  49. var value = AssertOnlyScopeContains(info.CurrentScopeChain, "globalLet", DebugScopeType.Global);
  50. Assert.Equal("test", value.AsString());
  51. });
  52. }
  53. [Fact]
  54. public void GlobalScopeIncludesGlobalVar()
  55. {
  56. string script = @"
  57. var globalVar = 'test';
  58. debugger;";
  59. TestHelpers.TestAtBreak(script, info =>
  60. {
  61. var value = AssertOnlyScopeContains(info.CurrentScopeChain, "globalVar", DebugScopeType.Global);
  62. Assert.Equal("test", value.AsString());
  63. });
  64. }
  65. [Fact]
  66. public void TopLevelBlockScopeIsIdentified()
  67. {
  68. string script = @"
  69. function test()
  70. {
  71. const localConst = 'test';
  72. debugger;
  73. }
  74. test();";
  75. TestHelpers.TestAtBreak(script, info =>
  76. {
  77. Assert.Equal(3, info.CurrentScopeChain.Count);
  78. Assert.Equal(DebugScopeType.Block, info.CurrentScopeChain[0].ScopeType);
  79. Assert.True(info.CurrentScopeChain[0].IsTopLevel);
  80. });
  81. }
  82. [Fact]
  83. public void NonTopLevelBlockScopeIsIdentified()
  84. {
  85. string script = @"
  86. function test()
  87. {
  88. {
  89. const localConst = 'test';
  90. debugger;
  91. }
  92. }
  93. test();";
  94. TestHelpers.TestAtBreak(script, info =>
  95. {
  96. // We only have 3 scopes, because the function top level block scope is empty.
  97. Assert.Equal(3, info.CurrentScopeChain.Count);
  98. Assert.Equal(DebugScopeType.Block, info.CurrentScopeChain[0].ScopeType);
  99. Assert.False(info.CurrentScopeChain[0].IsTopLevel);
  100. });
  101. }
  102. [Fact]
  103. public void BlockScopeIncludesLocalConst()
  104. {
  105. string script = @"
  106. function test()
  107. {
  108. {
  109. const localConst = 'test';
  110. debugger;
  111. }
  112. }
  113. test();";
  114. TestHelpers.TestAtBreak(script, info =>
  115. {
  116. var value = AssertOnlyScopeContains(info.CurrentScopeChain, "localConst", DebugScopeType.Block);
  117. Assert.Equal("test", value.AsString());
  118. });
  119. }
  120. [Fact]
  121. public void BlockScopeIncludesLocalLet()
  122. {
  123. string script = @"
  124. function test()
  125. {
  126. {
  127. let localLet = 'test';
  128. debugger;
  129. }
  130. }
  131. test();";
  132. TestHelpers.TestAtBreak(script, info =>
  133. {
  134. var value = AssertOnlyScopeContains(info.CurrentScopeChain, "localLet", DebugScopeType.Block);
  135. Assert.Equal("test", value.AsString());
  136. });
  137. }
  138. [Fact]
  139. public void LocalScopeIncludesLocalVar()
  140. {
  141. string script = @"
  142. function test()
  143. {
  144. var localVar = 'test';
  145. debugger;
  146. }
  147. test();";
  148. TestHelpers.TestAtBreak(script, info =>
  149. {
  150. AssertOnlyScopeContains(info.CurrentScopeChain, "localVar", DebugScopeType.Local);
  151. });
  152. }
  153. [Fact]
  154. public void LocalScopeIncludesBlockVar()
  155. {
  156. string script = @"
  157. function test()
  158. {
  159. debugger;
  160. {
  161. var localVar = 'test';
  162. }
  163. }
  164. test();";
  165. TestHelpers.TestAtBreak(script, info =>
  166. {
  167. AssertOnlyScopeContains(info.CurrentScopeChain, "localVar", DebugScopeType.Local);
  168. });
  169. }
  170. [Fact]
  171. public void BlockScopedConstIsVisibleInsideBlock()
  172. {
  173. string script = @"
  174. 'dummy statement';
  175. {
  176. const blockConst = 'block';
  177. debugger; // const isn't initialized until declaration
  178. }";
  179. TestHelpers.TestAtBreak(script, info =>
  180. {
  181. AssertOnlyScopeContains(info.CurrentScopeChain, "blockConst", DebugScopeType.Block);
  182. });
  183. }
  184. [Fact]
  185. public void BlockScopedLetIsVisibleInsideBlock()
  186. {
  187. string script = @"
  188. 'dummy statement';
  189. {
  190. let blockLet = 'block';
  191. debugger; // let isn't initialized until declaration
  192. }";
  193. TestHelpers.TestAtBreak(script, info =>
  194. {
  195. AssertOnlyScopeContains(info.CurrentScopeChain, "blockLet", DebugScopeType.Block);
  196. });
  197. }
  198. [Fact]
  199. public void HasCorrectScopeChainForFunction()
  200. {
  201. string script = @"
  202. function add(a, b)
  203. {
  204. debugger;
  205. return a + b;
  206. }
  207. const x = 1;
  208. const y = 2;
  209. const z = add(x, y);";
  210. TestHelpers.TestAtBreak(script, info =>
  211. {
  212. Assert.Collection(info.CurrentScopeChain,
  213. scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "b"),
  214. scope => AssertScope(scope, DebugScopeType.Global, "x", "y", "z", "add"));
  215. });
  216. }
  217. [Fact]
  218. public void HasCorrectScopeChainForNestedFunction()
  219. {
  220. string script = @"
  221. function add(a, b)
  222. {
  223. function power(a)
  224. {
  225. debugger;
  226. return a * a;
  227. }
  228. return power(a) + b;
  229. }
  230. const x = 1;
  231. const y = 2;
  232. const z = add(x, y);";
  233. TestHelpers.TestAtBreak(script, info =>
  234. {
  235. Assert.Collection(info.CurrentScopeChain,
  236. scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a"),
  237. scope => AssertScope(scope, DebugScopeType.Closure, "b", "power"), // a, this, arguments shadowed by local
  238. scope => AssertScope(scope, DebugScopeType.Global, "x", "y", "z", "add"));
  239. });
  240. }
  241. [Fact]
  242. public void HasCorrectScopeChainForBlock()
  243. {
  244. string script = @"
  245. function add(a, b)
  246. {
  247. if (a > 0)
  248. {
  249. const y = b / a;
  250. debugger;
  251. }
  252. return a + b;
  253. }
  254. const x = 1;
  255. const y = 2;
  256. const z = add(x, y);";
  257. TestHelpers.TestAtBreak(script, info =>
  258. {
  259. Assert.Collection(info.CurrentScopeChain,
  260. scope => AssertScope(scope, DebugScopeType.Block, "y"),
  261. scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "b"),
  262. scope => AssertScope(scope, DebugScopeType.Global, "x", "z", "add")); // y shadowed
  263. });
  264. }
  265. [Fact]
  266. public void HasCorrectScopeChainForNestedBlock()
  267. {
  268. string script = @"
  269. function add(a, b)
  270. {
  271. if (a > 0)
  272. {
  273. const y = b / a;
  274. if (y > 0)
  275. {
  276. const x = b / y;
  277. debugger;
  278. }
  279. }
  280. return a + b;
  281. }
  282. const x = 1;
  283. const y = 2;
  284. const z = add(x, y);";
  285. TestHelpers.TestAtBreak(script, info =>
  286. {
  287. Assert.Collection(info.CurrentScopeChain,
  288. scope => AssertScope(scope, DebugScopeType.Block, "x"),
  289. scope => AssertScope(scope, DebugScopeType.Block, "y"),
  290. scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "b"),
  291. scope => AssertScope(scope, DebugScopeType.Global, "z", "add")); // x, y shadowed
  292. });
  293. }
  294. [Fact]
  295. public void HasCorrectScopeChainForCatch()
  296. {
  297. string script = @"
  298. function func()
  299. {
  300. let a = 1;
  301. try
  302. {
  303. throw new Error('test');
  304. }
  305. catch (error)
  306. {
  307. debugger;
  308. }
  309. }
  310. func();";
  311. TestHelpers.TestAtBreak(script, info =>
  312. {
  313. Assert.Collection(info.CurrentScopeChain,
  314. scope => AssertScope(scope, DebugScopeType.Catch, "error"),
  315. scope => AssertScope(scope, DebugScopeType.Block, "a"),
  316. scope => AssertScope(scope, DebugScopeType.Local, "arguments"),
  317. scope => AssertScope(scope, DebugScopeType.Global, "func"));
  318. });
  319. }
  320. [Fact]
  321. public void HasCorrectScopeChainForWith()
  322. {
  323. string script = @"
  324. const obj = { a: 2, b: 4 };
  325. with (obj)
  326. {
  327. const x = a;
  328. debugger;
  329. };";
  330. TestHelpers.TestAtBreak(script, info =>
  331. {
  332. Assert.Collection(info.CurrentScopeChain,
  333. scope => AssertScope(scope, DebugScopeType.Block, "x"),
  334. scope => AssertScope(scope, DebugScopeType.With, "a", "b"),
  335. scope => AssertScope(scope, DebugScopeType.Global, "obj"));
  336. });
  337. }
  338. [Fact]
  339. public void ScopeChainIncludesNonEmptyScopes()
  340. {
  341. string script = @"
  342. const x = 2;
  343. if (x > 0)
  344. {
  345. const y = x;
  346. if (x > 1)
  347. {
  348. const z = x;
  349. debugger;
  350. }
  351. }";
  352. TestHelpers.TestAtBreak(script, info =>
  353. {
  354. Assert.Collection(info.CurrentScopeChain,
  355. scope => AssertScope(scope, DebugScopeType.Block, "z"),
  356. scope => AssertScope(scope, DebugScopeType.Block, "y"),
  357. scope => AssertScope(scope, DebugScopeType.Global, "x"));
  358. });
  359. }
  360. [Fact]
  361. public void ScopeChainExcludesEmptyScopes()
  362. {
  363. string script = @"
  364. const x = 2;
  365. if (x > 0)
  366. {
  367. if (x > 1)
  368. {
  369. const z = x;
  370. debugger;
  371. }
  372. }";
  373. TestHelpers.TestAtBreak(script, info =>
  374. {
  375. Assert.Collection(info.CurrentScopeChain,
  376. scope => AssertScope(scope, DebugScopeType.Block, "z"),
  377. scope => AssertScope(scope, DebugScopeType.Global, "x"));
  378. });
  379. }
  380. [Fact]
  381. public void ResolvesScopeChainsUpTheCallStack()
  382. {
  383. string script = @"
  384. const x = 1;
  385. function foo(a, c)
  386. {
  387. debugger;
  388. }
  389. function bar(b)
  390. {
  391. foo(b, 2);
  392. }
  393. bar(x);";
  394. TestHelpers.TestAtBreak(script, info =>
  395. {
  396. Assert.Collection(info.CallStack,
  397. frame => Assert.Collection(frame.ScopeChain,
  398. // in foo()
  399. scope => AssertScope(scope, DebugScopeType.Local, "arguments", "a", "c"),
  400. scope => AssertScope(scope, DebugScopeType.Global, "x", "foo", "bar")
  401. ),
  402. frame => Assert.Collection(frame.ScopeChain,
  403. // in bar()
  404. scope => AssertScope(scope, DebugScopeType.Local, "arguments", "b"),
  405. scope => AssertScope(scope, DebugScopeType.Global, "x", "foo", "bar")
  406. ),
  407. frame => Assert.Collection(frame.ScopeChain,
  408. // in global
  409. scope => AssertScope(scope, DebugScopeType.Global, "x", "foo", "bar")
  410. )
  411. );
  412. });
  413. }
  414. }
  415. }