Labels.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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" : "Expected identifier",
  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 = `function f() {
  210. // Bad label, verify consistency in deferred parsing
  211. a():
  212. var i = 0;
  213. }
  214. f();`
  215. function testModuleScript(source, message, shouldFail = false) {
  216. let testfunc = () => testRunner.LoadModule(source, 'samethread', shouldFail);
  217. if (shouldFail) {
  218. let caught = false;
  219. // We can't use assert.throws here because the SyntaxError used to construct the thrown error
  220. // is from a different context so it won't be strictly equal to our SyntaxError.
  221. try {
  222. testfunc();
  223. } catch(e) {
  224. caught = true;
  225. // Compare toString output of SyntaxError and other context SyntaxError constructor.
  226. assert.areEqual(e.constructor.toString(), SyntaxError.toString(), message);
  227. }
  228. assert.isTrue(caught, `Expected error not thrown: ${message}`);
  229. } else {
  230. assert.doesNotThrow(testfunc, message);
  231. }
  232. }
  233. var tests = [
  234. {
  235. name : "Labels followed by declarations which should all be syntax errors.",
  236. body : function ()
  237. {
  238. assert.throws(() => eval("a: let x;"), SyntaxError,
  239. "A let declaration must not be labelled", "Labels not allowed before lexical declaration");
  240. assert.throws(() => eval("b: const y = 0;"), SyntaxError,
  241. "A const declaration must not be labelled", "Labels not allowed before lexical declaration");
  242. assert.throws(() => eval("c: class z {}"), SyntaxError,
  243. "An class declaration must not be labelled", "Labels not allowed before class declaration");
  244. assert.throws(() => eval("d: function* w() {}"), SyntaxError,
  245. "A generator must not be labelled", "Labels not allowed before generator declaration");
  246. assert.throws(() => eval("e: async function u() { }"), SyntaxError,
  247. "An async function must not be labelled", "Labels not allowed before async function declaration");
  248. /*assert.throws(() => eval("e: async function* u() { }"), SyntaxError, //TODO: Uncomment when async generators supported.
  249. "An async generator must not be labelled", "Labels not allowed before async function declaration");*/
  250. assert.throws(() => eval("let x=5; labelA: "), SyntaxError, "Labels must not be followed by EOF", "Unexpected end of script after a label.");
  251. }
  252. },
  253. {
  254. name : "Labels followed by non-declarations are valid syntax (non-generator non-async function declarations and non-lexical variable declarations are allowed)",
  255. body : function ()
  256. {
  257. assert.doesNotThrow(() => eval(testFunctionAfterLabel), "Function declarations should be allowed after a label.");
  258. assert.doesNotThrow(() => eval(testVarAfterLabel), "var declarations should be allowed after a label.");
  259. assert.doesNotThrow(() => eval(testForLoopAfterLabel), "for loop statements should be allowed after a label.");
  260. assert.doesNotThrow(() => eval(testNonLexicalDeclarationAfterLabel), "non-lexical declarations should be allowed after a label.");
  261. assert.doesNotThrow(() => eval(testWhileLoopAfterLabel), "while loops should be allowed after a label");
  262. assert.doesNotThrow(() => eval(testBlockStatementAfterLabel), "block statements should be allowed after a label");
  263. assert.doesNotThrow(() => eval(testEmptyStatementAfterLabel), "empty statements should be allowed after a label");
  264. assert.doesNotThrow(() => eval(testIfStatementAfterLabel), "if statements should be allowed after a label");
  265. assert.doesNotThrow(() => eval(testSwitchStatementAfterLabel), "switch statements should be allowed after a label");
  266. assert.doesNotThrow(() => eval(testContinueAfterLabel), "continue statements should be allowed after a label");
  267. assert.doesNotThrow(() => eval(testBreakAfterLabel), "break statements should be allowed after a label");
  268. assert.doesNotThrow(() => eval(testNestedLabels), "labelled statements should be allowed after a label");
  269. assert.doesNotThrow(() => eval(testThrowAfterLabel), "throw statements should be allowed after a label");
  270. assert.doesNotThrow(() => eval(testTryStatementAfterLabel), "throw statements should be allowed after a label");
  271. }
  272. },
  273. {
  274. name : "Label tests with strict mode applied",
  275. body : function ()
  276. {
  277. 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.");
  278. }
  279. },
  280. {
  281. name : "Label tests with keywords as labels",
  282. body : function ()
  283. {
  284. testModuleScript("await: for(let i = 0; i < 3; i++){break await;}", "'await' expression not allowed in this context", shouldFail=true)
  285. 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")
  286. assert.doesNotThrow(() => eval(strictMode + testAwaitAsLabelOutsideAsyncFnc), "'await' should be allowed as label outside of async functions, even in strict mode.")
  287. 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.");
  288. 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
  289. assert.throws(() => eval(strictMode + "with" + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier 'with'", "'with' statements are not allowed in strict mode");
  290. for(let label in invalidLabels)
  291. {
  292. assert.throws(() => eval(label + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier '" + label + "'", invalidLabels[label]);
  293. assert.throws(() => eval(strictMode + label + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier '" + label + "'", invalidLabels[label]);
  294. }
  295. for(let invalidLabelInStrict in strictModeOnlyInvalidLabels)
  296. {
  297. assert.throws(() =>eval(strictMode + invalidLabelInStrict + testIfLabelIsValid), SyntaxError, "Expected syntax error in strict mode for future reserved keyword '" + invalidLabelInStrict + "'", strictModeOnlyInvalidLabels[invalidLabelInStrict])
  298. assert.doesNotThrow(() =>eval(invalidLabelInStrict + testIfLabelIsValid), "Expected no syntax error for future reserved keyword '" + invalidLabelInStrict + " in non-strict mode")
  299. }
  300. }
  301. },
  302. {
  303. name : "Label tests where label is invalid syntax",
  304. body : function ()
  305. {
  306. assert.throws(() => eval(testInvalidLabelSyntaxParens), SyntaxError, "Expected syntax error from using malformed label", "Expected ';'")
  307. assert.throws(() => eval(testInvalidLabelSyntaxIncrement), SyntaxError, "Expected syntax error from using malformed label", "Expected ';'")
  308. assert.throws(() => eval(testInvalidLabelSyntaxDecrement), SyntaxError, "Expected syntax error from using malformed label", "Expected ';'")
  309. assert.throws(() => eval(testInvalidLabelSyntaxNonIDContinueCharacterInLabel), SyntaxError, "Expected syntax error from using malformed label", "Expected identifier")
  310. assert.throws(() => eval(testInvalidLabelSyntaxArrayAccess), SyntaxError, "Expected syntax error from using malformed label", "Expected ';'")
  311. assert.throws(() => eval(testInvalidLabelSyntaxFunctionCall), SyntaxError, "Expected syntax error from using malformed label", "Expected ';'")
  312. }
  313. }
  314. ];
  315. testRunner.runTests(tests, {
  316. verbose : WScript.Arguments[0] != "summary"
  317. });