Labels.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  6. strictMode = "'use strict'\n"
  7. testFunctionAfterLabel =
  8. `a:
  9. function x()
  10. {
  11. }`
  12. testVarAfterLabel =
  13. `b:
  14. var a;`
  15. testForLoopAfterLabel =
  16. `c:
  17. for(i = 0; i < 10; i++)
  18. {
  19. d:
  20. for(j = 0; j < 10; j++)
  21. {
  22. if((i%j)%3 === 0)
  23. break d;
  24. }
  25. }`
  26. testWhileLoopAfterLabel =
  27. `i = 1, j = 1, product = 1
  28. c:
  29. while(i <= 20)
  30. {
  31. d:
  32. while(j <= 5)
  33. {
  34. j = j+2
  35. product *= i*j;
  36. break c;
  37. }
  38. i = i+1
  39. }
  40. assert.areEqual(3, product)
  41. `
  42. testNonLexicalDeclarationAfterLabel=
  43. `d:
  44. g = "Hola, Mundo."`
  45. testBlockStatementAfterLabel=
  46. `e:
  47. {
  48. }`
  49. testEmptyStatementAfterLabel=
  50. `f:
  51. ;`
  52. testIfStatementAfterLabel=
  53. `i = 3
  54. g:
  55. if(i > 4)
  56. i -= 2;
  57. else
  58. i += 2;`
  59. testSwitchStatementAfterLabel=
  60. `var f = 'paper'
  61. switchLabel:
  62. switch(f){
  63. case 'Paper':
  64. break;
  65. default:
  66. f = null;
  67. }`
  68. testContinueAfterLabel =
  69. `c:
  70. for(i = 0; i < 10; i++)
  71. {
  72. d:
  73. continue c;
  74. }`
  75. testBreakAfterLabel =
  76. `c:
  77. for(i = 0; i < 10; i++)
  78. {
  79. d:
  80. break c;
  81. }`
  82. testNestedLabels =
  83. `a:
  84. b:
  85. c:
  86. d:
  87. e:
  88. f:
  89. g:
  90. z:
  91. var y = 6;`
  92. testThrowAfterLabel =
  93. `var y = 0
  94. if(y > 5)
  95. {
  96. a:
  97. throw("shouldn't be here")
  98. }`
  99. testTryStatementAfterLabel =
  100. `var y = 0
  101. g:
  102. try{
  103. y /= 0
  104. }
  105. catch(e){
  106. print(e)
  107. }`
  108. testAwaitAsLabelOutsideAsyncFnc =
  109. `await:
  110. for(i = 0; i < 3; i++)
  111. {
  112. continue await;
  113. }`
  114. testAwaitAsLabelInsideAsyncFnc =
  115. `async function main() {
  116. await:
  117. var i = 10;
  118. i **= 2;
  119. }`
  120. strictModeOnlyInvalidLabels = {//Maps the reserved word to the error message given when it is used as a label in strict mode
  121. "implements" : "Syntax error",
  122. "interface" : "Syntax error",
  123. "let" : "Syntax error",
  124. "package" : "Syntax error",
  125. "private" : "Syntax error",
  126. "protected" : "Syntax error",
  127. "public" : "Syntax error",
  128. "static" : "Syntax error",
  129. "yield" : "Syntax error"
  130. }
  131. invalidLabels= { //Maps the reserved word to the error message given when it is used as a label in either non-strict or strict mode
  132. "break" : "Can't have 'break' outside of loop",
  133. "case" : "Syntax error",
  134. "catch" : "Syntax error",
  135. "class" : "Expected identifier",
  136. "const" : "Expected identifier",
  137. "continue" : "Can't have 'continue' outside of loop",
  138. "debugger" : "Expected ';'",
  139. "default" : "Syntax error",
  140. "delete" : "Syntax error",
  141. "do" : "Syntax error",
  142. "else" : "Syntax error",
  143. "export" : "Syntax error",
  144. "extends" : "Syntax error",
  145. "finally" : "Syntax error",
  146. "for" : "Expected '('",
  147. "function" : "Expected identifier",
  148. "if" : "Expected '('",
  149. "import" : "Module import or export statement unexpected here",
  150. "in" : "Syntax error",
  151. "instanceof" : "Syntax error",
  152. "new" : "Syntax error",
  153. "return" : "'return' statement outside of function",
  154. "super" : "Invalid use of the 'super' keyword",
  155. "switch" : "Expected '('",
  156. "this" : "Expected ';'",
  157. "throw" : "Syntax error",
  158. "try" : "Expected '{'",
  159. "typeof" : "Syntax error",
  160. "var" : "Expected identifier",
  161. "void" : "Syntax error",
  162. "while" : "Expected '('",
  163. "enum" : "Syntax error",
  164. "null" : "Expected ';'",
  165. "true" : "Expected ';'",
  166. "false": "Expected ';'"
  167. }
  168. testIfLabelIsValid =
  169. `:
  170. for(let i = 0; i < 3; i++)
  171. {
  172. i = i+1
  173. }`
  174. testInvalidLabelSyntaxParens =
  175. `function f() {
  176. // Label in parenthesis is bad syntax. Verify consistency in deferred parsing.
  177. (a):
  178. var i = 0;
  179. }
  180. f();`
  181. testInvalidLabelSyntaxIncrement =
  182. `function f() {
  183. // Bad label, verify consistency in deferred parsing
  184. a++:
  185. var i = 0;
  186. }
  187. f();`
  188. testInvalidLabelSyntaxDecrement =
  189. `function f() {
  190. // Bad label, verify consistency in deferred parsing
  191. a--:
  192. var i = 0;
  193. }
  194. f();`
  195. testInvalidLabelSyntaxNonIDContinueCharacterInLabel =
  196. `function f() {
  197. // Bad label, verify consistency in deferred parsing
  198. a.:
  199. var i = 0;
  200. }
  201. f();`
  202. testInvalidLabelSyntaxArrayAccess =
  203. `function f() {
  204. // Bad label, verify consistency in deferred parsing
  205. a[0]:
  206. var i = 0;
  207. }
  208. f();`
  209. testInvalidLabelSyntaxFunctionCall =
  210. `function f() {
  211. // Bad label, verify consistency in deferred parsing
  212. a():
  213. var i = 0;
  214. }
  215. f();`
  216. testRuntimeErrorWithDanglingLetAfterLabel =
  217. `if (true) {
  218. L: let // comment
  219. {}
  220. }`
  221. testNoSyntaxErrorWithDanglingLetAfterLabel =
  222. `if (false) {
  223. L: let // comment
  224. {}
  225. }`
  226. testRuntimeErrorWithDanglingLetAfterLabel2 =
  227. `if (true) {
  228. L: let // ASI
  229. var g = {}
  230. }`
  231. testNoSyntaxErrorWithDanglingLetAfterLabel2 =
  232. `if (false) {
  233. L: let // ASI
  234. var g = {}
  235. }`
  236. testRuntimeErrorWithDanglingLetAfterLabel3=
  237. `if (true) {
  238. L: let // ASI
  239. let a = 3
  240. }`
  241. testNoSyntaxErrorWithDanglingLetAfterLabel3=
  242. `if (false) {
  243. L: let // ASI
  244. let a = 3
  245. }`
  246. testRuntimeErrorWithDanglingLetAfterLabel4=
  247. `if (true) {
  248. L: let // ASI
  249. a = 3
  250. }`
  251. testNoSyntaxErrorWithDanglingLetAfterLabel4=
  252. `if (false) {
  253. L: let // ASI
  254. a = 3
  255. }`
  256. testNoRuntimeErrorWithDanglingVarAfterLabel=
  257. `label: var
  258. x = 0;`
  259. testSyntaxErrorWithDanglingConstAfterLabel=
  260. `label: const
  261. x = 0;`
  262. testRuntimeErrorWithDanglingYieldAfterLabel=
  263. `if (true) {
  264. L: yield // ASI
  265. console.log("b");
  266. 1;
  267. }`
  268. testNoSyntaxErrorWithDanglingYieldAfterLabel=
  269. `if (false) {
  270. L: yield // ASI
  271. console.log("b");
  272. 1;
  273. }`
  274. testGeneratorWithDanglingYieldAfterLabel=
  275. `function* gen(num)
  276. {
  277. L: yield // ASI
  278. ++num
  279. }
  280. const iterator = gen(0)
  281. if(iterator.next().value === 1)
  282. throw("Yield should not return a number");
  283. `
  284. testNoSyntaxErrorWithDanglingAwaitAfterLabel=
  285. `function fn()
  286. {
  287. for(i = 0; i < 30; i++);
  288. }
  289. if(false)
  290. {
  291. L: await
  292. fn()
  293. }`
  294. testRuntimeErrorWithDanglingAwaitAfterLabel=
  295. `function fn()
  296. {
  297. for(i = 0; i < 30; i++);
  298. }
  299. if(true)
  300. {
  301. L: await
  302. fn()
  303. }`
  304. testNoSyntaxErrorWithDanglingStaticAfterLabel=
  305. `function g(){
  306. L: static
  307. x = 0
  308. }`
  309. testRuntimeErrorWithDanglingStaticAfterLabel=
  310. `L: static
  311. x = 0`
  312. testSyntaxErrorWithContinueAfterLabel=
  313. `function fn() {
  314. L:
  315. continue L;
  316. }`
  317. testNoLabelNotFoundWithBreakAfterLabel=
  318. `function fn() {
  319. L:
  320. break L;
  321. }`
  322. function testModuleScript(source, message, shouldFail = false) {
  323. let testfunc = () => testRunner.LoadModule(source, 'samethread', shouldFail);
  324. if (shouldFail) {
  325. let caught = false;
  326. // We can't use assert.throws here because the SyntaxError used to construct the thrown error
  327. // is from a different context so it won't be strictly equal to our SyntaxError.
  328. try {
  329. testfunc();
  330. } catch(e) {
  331. caught = true;
  332. // Compare toString output of SyntaxError and other context SyntaxError constructor.
  333. assert.areEqual(e.constructor.toString(), SyntaxError.toString(), message);
  334. }
  335. assert.isTrue(caught, `Expected error not thrown: ${message}`);
  336. } else {
  337. assert.doesNotThrow(testfunc, message);
  338. }
  339. }
  340. var tests = [
  341. {
  342. name : "Labels followed by declarations which should all be syntax errors.",
  343. body : function ()
  344. {
  345. assert.throws(() => eval("a: let x;"), SyntaxError,
  346. "A let declaration must not be labelled", "Labels not allowed before lexical declaration");
  347. assert.throws(() => eval("b: const y = 0;"), SyntaxError,
  348. "A const declaration must not be labelled", "Labels not allowed before lexical declaration");
  349. assert.throws(() => eval("c: class z {}"), SyntaxError,
  350. "An class declaration must not be labelled", "Labels not allowed before class declaration");
  351. assert.throws(() => eval("d: function* w() {}"), SyntaxError,
  352. "A generator must not be labelled", "Labels not allowed before generator declaration");
  353. assert.throws(() => eval("e: async function u() { }"), SyntaxError,
  354. "An async function must not be labelled", "Labels not allowed before async function declaration");
  355. /*assert.throws(() => eval("e: async function* u() { }"), SyntaxError, //TODO: Uncomment when async generators supported.
  356. "An async generator must not be labelled", "Labels not allowed before async function declaration");*/
  357. assert.throws(() => eval("let x=5; labelA: "), SyntaxError, "Labels must not be followed by EOF", "Unexpected end of script after a label.");
  358. }
  359. },
  360. {
  361. name : "Labels followed by non-declarations are valid syntax (non-generator non-async function declarations and non-lexical variable declarations are allowed)",
  362. body : function ()
  363. {
  364. assert.doesNotThrow(() => eval(testFunctionAfterLabel), "Function declarations should be allowed after a label.");
  365. assert.doesNotThrow(() => eval(testVarAfterLabel), "var declarations should be allowed after a label.");
  366. assert.doesNotThrow(() => eval(testForLoopAfterLabel), "for loop statements should be allowed after a label.");
  367. assert.doesNotThrow(() => eval(testNonLexicalDeclarationAfterLabel), "non-lexical declarations should be allowed after a label.");
  368. assert.doesNotThrow(() => eval(testWhileLoopAfterLabel), "while loops should be allowed after a label");
  369. assert.doesNotThrow(() => eval(testBlockStatementAfterLabel), "block statements should be allowed after a label");
  370. assert.doesNotThrow(() => eval(testEmptyStatementAfterLabel), "empty statements should be allowed after a label");
  371. assert.doesNotThrow(() => eval(testIfStatementAfterLabel), "if statements should be allowed after a label");
  372. assert.doesNotThrow(() => eval(testSwitchStatementAfterLabel), "switch statements should be allowed after a label");
  373. assert.doesNotThrow(() => eval(testContinueAfterLabel), "continue statements should be allowed after a label");
  374. assert.doesNotThrow(() => eval(testBreakAfterLabel), "break statements should be allowed after a label");
  375. assert.doesNotThrow(() => eval(testNestedLabels), "labelled statements should be allowed after a label");
  376. assert.doesNotThrow(() => eval(testThrowAfterLabel), "throw statements should be allowed after a label");
  377. assert.doesNotThrow(() => eval(testTryStatementAfterLabel), "throw statements should be allowed after a label");
  378. }
  379. },
  380. {
  381. name : "Label tests with strict mode applied",
  382. body : function ()
  383. {
  384. assert.throws(() => eval(strictMode + testFunctionAfterLabel), SyntaxError, "Function declarations should not be allowed after a label in strict mode.", "Function declarations not allowed after a label in strict mode.");
  385. }
  386. },
  387. {
  388. name : "Label tests with keywords as labels",
  389. body : function ()
  390. {
  391. testModuleScript("await: for(let i = 0; i < 3; i++){break await;}", "'await' expression not allowed in this context", shouldFail=true)
  392. assert.doesNotThrow(() => eval(strictMode + "await: for(let i = 0; i < 3; i++){break await;}"), "Await as label should only be an error when in a module")
  393. assert.doesNotThrow(() => eval(strictMode + testAwaitAsLabelOutsideAsyncFnc), "'await' should be allowed as label outside of async functions, even in strict mode.")
  394. assert.throws(() => eval(strictMode + testAwaitAsLabelInsideAsyncFnc), SyntaxError, "Expected 'await' label in async function to be a syntax error.", "Use of 'await' as label in async function is not allowed.");
  395. assert.throws(() => eval("with" + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier 'with'", "Expected '('"); //The only invalid keyword with a different error message for strict/non-strict modes
  396. assert.throws(() => eval(strictMode + "with" + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier 'with'", "'with' statements are not allowed in strict mode");
  397. for(let label in invalidLabels)
  398. {
  399. assert.throws(() => eval(label + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier '" + label + "'");
  400. assert.throws(() => eval(strictMode + label + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier '" + label + "'");
  401. }
  402. for(let invalidLabelInStrict in strictModeOnlyInvalidLabels)
  403. {
  404. assert.throws(() => eval(strictMode + invalidLabelInStrict + testIfLabelIsValid), SyntaxError, "Expected syntax error in strict mode for future reserved keyword '" + invalidLabelInStrict + "'")
  405. assert.doesNotThrow(() => eval(invalidLabelInStrict + testIfLabelIsValid), "Expected no syntax error for future reserved keyword '" + invalidLabelInStrict + " in non-strict mode")
  406. }
  407. }
  408. },
  409. {
  410. name : "Label tests where label is invalid syntax",
  411. body : function ()
  412. {
  413. assert.throws(() => eval(testInvalidLabelSyntaxParens), SyntaxError, "Expected syntax error from using malformed label", "Expected ';'")
  414. assert.throws(() => eval(testInvalidLabelSyntaxIncrement), SyntaxError, "Expected syntax error from using malformed label", "Expected ';'")
  415. assert.throws(() => eval(testInvalidLabelSyntaxDecrement), SyntaxError, "Expected syntax error from using malformed label", "Expected ';'")
  416. assert.throws(() => eval(testInvalidLabelSyntaxNonIDContinueCharacterInLabel), SyntaxError, "Expected syntax error from using malformed label", "Expected identifier")
  417. assert.throws(() => eval(testInvalidLabelSyntaxArrayAccess), SyntaxError, "Expected syntax error from using malformed label", "Expected ';'")
  418. assert.throws(() => eval(testInvalidLabelSyntaxFunctionCall), SyntaxError, "Expected syntax error from using malformed label", "Expected ';'")
  419. }
  420. },
  421. {
  422. name : "Label tests, edge cases",
  423. body : function ()
  424. {
  425. assert.throws(() => eval(testRuntimeErrorWithDanglingLetAfterLabel), ReferenceError, "Expected runtime error from using let as identifier", "'let' is not defined")
  426. assert.doesNotThrow(() => eval(testNoSyntaxErrorWithDanglingLetAfterLabel), "Expected no syntax error from using let as identifier after label")
  427. assert.throws(() => eval(testRuntimeErrorWithDanglingLetAfterLabel2), ReferenceError, "Expected runtime error from using let as identifier", "'let' is not defined")
  428. assert.doesNotThrow(() => eval(testNoSyntaxErrorWithDanglingLetAfterLabel2), "Expected no syntax error from using let as identifier after label")
  429. assert.throws(() => eval(testRuntimeErrorWithDanglingLetAfterLabel3), ReferenceError, "Expected runtime error from using let as identifier", "'let' is not defined")
  430. assert.doesNotThrow(() => eval(testNoSyntaxErrorWithDanglingLetAfterLabel3), "Expected no syntax error from using let as identifier after label")
  431. assert.throws(() => eval(testRuntimeErrorWithDanglingLetAfterLabel4), ReferenceError, "Expected runtime error from using let as identifier", "'let' is not defined")
  432. assert.doesNotThrow(() => eval(testNoSyntaxErrorWithDanglingLetAfterLabel4), "Expected no syntax error from using let as identifier after label")
  433. assert.doesNotThrow(() => eval(testNoRuntimeErrorWithDanglingVarAfterLabel), "Expected no syntax error from using var after label")
  434. assert.throws(() => eval(testSyntaxErrorWithDanglingConstAfterLabel), SyntaxError, "Expected syntax error from using const after label", "Labels not allowed before lexical declaration")
  435. assert.doesNotThrow(() => eval(testNoSyntaxErrorWithDanglingYieldAfterLabel), "Expected no syntax error from using yield after label")
  436. assert.throws(() => eval(testRuntimeErrorWithDanglingYieldAfterLabel), ReferenceError, "Expected runtime error for undefined reference to yield", "'yield' is not defined")
  437. assert.doesNotThrow(() => eval(testGeneratorWithDanglingYieldAfterLabel), "Expected no error from using yield after label. Also expect the yield to not be bound to the expression.")
  438. assert.doesNotThrow(() => eval(testNoSyntaxErrorWithDanglingAwaitAfterLabel), "Expected no error from using await after label. Also expect the yield to not be bound to the expression.")
  439. assert.throws(() => eval(testRuntimeErrorWithDanglingAwaitAfterLabel), ReferenceError, "Expected reference error from stranded await being used after label", "'await' is not defined")
  440. assert.throws(() => eval(testRuntimeErrorWithDanglingStaticAfterLabel), ReferenceError, "Expected reference error from stranded static being used after label", "'static' is not defined")
  441. assert.doesNotThrow(() => eval(testNoSyntaxErrorWithDanglingStaticAfterLabel), "Expected no issue parsing since static is viewed as an identifier")
  442. assert.throws(() => eval(testSyntaxErrorWithContinueAfterLabel), SyntaxError, "Expected syntax error from having continue outside of loop", "Can't have 'continue' outside of loop")
  443. assert.doesNotThrow(() => eval(testNoLabelNotFoundWithBreakAfterLabel), "Expected no issue from finding label")
  444. }
  445. }
  446. ];
  447. testRunner.runTests(tests, {
  448. verbose : WScript.Arguments[0] != "summary"
  449. });