SpecialSymbolCapture.js 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000
  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. var lambdaForGlobalThis = () => this;
  7. var tests = [
  8. {
  9. name: "Simple function defined at global scope with a this binding and global has no this reference",
  10. body: function () {
  11. WScript.LoadScript(`
  12. function bar() {
  13. this.o = 'bar';
  14. }
  15. var _this = { o: 'global' };
  16. bar.call(_this);
  17. assert.areEqual('bar', _this.o, "Function bar mutated _this, which was loaded as the 'this' binding inside bar");
  18. `);
  19. }
  20. },
  21. {
  22. name: "Simple binding of 'this' in global function",
  23. body: function () {
  24. WScript.LoadScript(`
  25. assert.areEqual(undefined, this.o, "Initial value of 'this.o' is undefined");
  26. this.o = 'global';
  27. assert.areEqual('global', this.o, "Writing property to 'this' binding in global function");
  28. `);
  29. }
  30. },
  31. {
  32. name: "Lambda capturing global 'this' binding. Note: This isn't really a capture.",
  33. body: function () {
  34. WScript.LoadScript(`
  35. var lambdaCapturesGlobalThis = str => this.o = str
  36. this.o = 'global';
  37. lambdaCapturesGlobalThis('called at global');
  38. assert.areEqual('called at global', this.o, "Lambda modifying global this binding called from global function");
  39. this.o = 'global';
  40. function foo() {
  41. lambdaCapturesGlobalThis('called from nested function');
  42. }
  43. foo();
  44. assert.areEqual('called from nested function', this.o, "Lambda modifying global this binding called from nested function");
  45. this.o = 'global';
  46. var globalLambda = () => lambdaCapturesGlobalThis('called from global lambda')
  47. globalLambda();
  48. assert.areEqual('called from global lambda', this.o, "Lambda modifying global this binding called from global lambda");
  49. this.o = 'global';
  50. eval("lambdaCapturesGlobalThis('called from global eval');");
  51. assert.areEqual('called from global eval', this.o, "Lambda modifying global this binding called from global eval");
  52. `);
  53. }
  54. },
  55. {
  56. name: "Function with eval and lambda both modifying 'this'",
  57. body: function () {
  58. WScript.LoadScript(`
  59. function bar() {
  60. eval('this.o = "eval bar"');
  61. (() => this.o = 'lambda bar')()
  62. }
  63. var _this = { o: 'global' };
  64. bar.call(_this);
  65. assert.areEqual('lambda bar', _this.o, "Lambda was the last thing to modify 'this' binding inside bar");
  66. `);
  67. }
  68. },
  69. {
  70. name: "Capture of nested 'this' object",
  71. body: function () {
  72. function foo() {
  73. var count = 0;
  74. this.o = 'foo';
  75. eval("count++; assert.areEqual('foo', this.o, 'Eval capture of nested-function this binding')");
  76. eval(`count++; eval("assert.areEqual('foo', this.o, 'Nested eval capture of nested-function this binding')")`);
  77. (() => {count++; assert.areEqual('foo', this.o, 'Lambda capture of nested function this binding')})();
  78. (() => () => {count++; assert.areEqual('foo', this.o, 'Nested lambda capture of nested function this binding')})()();
  79. (() => eval(`count++; assert.areEqual('foo', this.o, 'Lambda with eval capture of nested function this binding')`))();
  80. (() => eval(`count++; eval("assert.areEqual('foo', this.o, 'Lambda with nested eval capture of nested function this binding')")`))();
  81. eval(`(() => {count++; assert.areEqual('foo', this.o, 'Eval with lambda capture of nested-function this binding')})()`);
  82. eval("eval(`(() => {count++; assert.areEqual('foo', this.o, 'Nested eval with lambda capture of nested-function this binding')})()`)");
  83. eval("(() => eval(`(() => {count++; assert.areEqual('foo', this.o, 'Nested eval and nested lambda capture of nested-function this binding')})()`))()");
  84. eval("count++; assert.areEqual('foo', this.o, 'Eval with nested lambda in which the eval also references this binding'); (() => assert.areEqual('foo', this.o, 'Eval with lambda capture of nested-function this binding'))();");
  85. assert.areEqual(10, count, 'Called correct number of test functions');
  86. return true;
  87. }
  88. assert.isTrue(foo.call({o:'test'}), "Test function completes");
  89. }
  90. },
  91. {
  92. name: "Eval modifying 'this' inside a nested function which doesn't itself reference 'this' binding",
  93. body: function () {
  94. WScript.LoadScript(`
  95. function foo() {
  96. this.o = 'foo';
  97. function bar() {
  98. eval('this.o = "eval bar"');
  99. }
  100. bar();
  101. }
  102. this.o = 'global';
  103. foo();
  104. assert.areEqual('eval bar', this.o, "Eval was the last thing to modify 'this' binding inside bar");
  105. `);
  106. }
  107. },
  108. {
  109. name: "Various usage patterns for 'this'",
  110. body: function () {
  111. WScript.LoadScript(`
  112. eval('this.o = "global eval"');
  113. assert.areEqual('global eval', this.o, "Global eval modifying this");
  114. (() => this.o = 'global lambda')();
  115. assert.areEqual('global lambda', this.o, "Global lambda modifying this");
  116. function bar () {
  117. this.o = 'bar';
  118. return this;
  119. }
  120. var _this = bar();
  121. assert.areEqual('bar', this.o, "Nested function modifies it's own this binding");
  122. assert.areEqual(_this, this, "bar returns local this which is the same object as global this");
  123. function baz() {
  124. this.o = 'baz';
  125. return this;
  126. }
  127. _this = baz.call({});
  128. assert.areEqual('bar', this.o, "Function baz doesn't modify global this object");
  129. assert.isFalse(_this === this, "baz returns local this which is not the same object as global this");
  130. function bot(_this) {
  131. _this.o = 'bot';
  132. return _this;
  133. }
  134. _this = bot(this);
  135. assert.areEqual('bot', this.o, "Nested function modifies global this object via a different binding");
  136. assert.areEqual(_this, this, "bot returns local this which is the same object as global this");
  137. function foo() {
  138. assert.areEqual('global', this.o, "The global this binding is passed as the this argument to foo");
  139. this.o = 'foo';
  140. assert.areEqual('foo', this.o, "Simple property assignment");
  141. function bar() {
  142. this.o = 'bar';
  143. return this;
  144. }
  145. _this = bar.call(this);
  146. assert.areEqual('bar', this.o, "Nested function modifies outside this via it's own this binding");
  147. assert.areEqual(_this, this, "bar returns local this which is the same object as foo's this");
  148. function baz() {
  149. this.o = 'baz';
  150. return this;
  151. }
  152. _this = baz.call({});
  153. assert.areEqual('bar', this.o, "Nested function modifies it's own this binding");
  154. assert.isFalse(_this === this, "baz returns local this which is not the same object as foo's this");
  155. function bot(_this) {
  156. _this.o = 'bot';
  157. return _this;
  158. }
  159. _this = bot(this);
  160. assert.areEqual('bot', this.o, "Nested function modifies outside this via a different binding");
  161. assert.areEqual(_this, this, "bot returns local this which is the same object as foo's this");
  162. return this;
  163. }
  164. this.o = 'global';
  165. _this = foo.call(this);
  166. assert.areEqual(_this, this, "foo returns local this which is the same object as global this");
  167. `);
  168. }
  169. },
  170. {
  171. name: "Strict mode outside 'this' object is not captured by nested functions",
  172. body: function () {
  173. WScript.LoadScript(`
  174. "use strict";
  175. this.o = 'global';
  176. function bar() {
  177. assert.areEqual(undefined, this, "Global this object is not loaded by default in nested functions in strict mode");
  178. }
  179. bar();
  180. function baz() {
  181. this.o = 'baz';
  182. return this;
  183. }
  184. var _this = baz.call({});
  185. assert.isFalse(_this === this, "baz returns this which is not bound to global this");
  186. assert.areEqual('baz', _this.o, "baz returned this which was passed to it via call()");
  187. function foo() {
  188. assert.isTrue(this !== undefined, "foo must be called with an object or something");
  189. function bar() {
  190. assert.areEqual(undefined, this, "Outside this object is not loaded by default in nested functions in strict mode");
  191. }
  192. bar();
  193. }
  194. foo.call({});
  195. `);
  196. }
  197. },
  198. {
  199. name: "Special names are invalid assignment targets",
  200. body: function () {
  201. assert.throws(() => eval(`
  202. function f() {
  203. if (0) new.target = 1;
  204. }
  205. `), SyntaxError, "", "Invalid left-hand side in assignment.");
  206. assert.throws(() => eval(`
  207. function f() {
  208. if (0) this = 1;
  209. }
  210. `), SyntaxError, "", "Invalid left-hand side in assignment.");
  211. assert.throws(() => eval(`
  212. function f() {
  213. if (0) super = 1;
  214. }
  215. `), SyntaxError, "", "Invalid use of the 'super' keyword");
  216. }
  217. },
  218. {
  219. name: "Global-scope `new.target` keyword is early syntax error",
  220. body: function () {
  221. var syntaxError;
  222. assert.throws(() => WScript.LoadScript(`syntaxError = SyntaxError; new.target;`), syntaxError, "'new.target' is invalid syntax at global scope", "Invalid use of the 'new.target' keyword");
  223. assert.throws(() => WScript.LoadScript(`syntaxError = SyntaxError; () => new.target`), syntaxError, "'new.target' is invalid in arrow at global scope", "Invalid use of the 'new.target' keyword");
  224. assert.throws(() => WScript.LoadScript(`syntaxError = SyntaxError; () => () => new.target`), syntaxError, "'new.target' is invalid in nested arrow at global scope", "Invalid use of the 'new.target' keyword");
  225. assert.throws(() => WScript.LoadScript(`syntaxError = SyntaxError; eval('new.target');`), syntaxError, "'new.target' is invalid in eval at global scope", "Invalid use of the 'new.target' keyword");
  226. assert.throws(() => WScript.LoadScript(`syntaxError = SyntaxError; eval("eval('new.target');")`), syntaxError, "'new.target' is invalid in nested eval at global scope", "Invalid use of the 'new.target' keyword");
  227. assert.throws(() => WScript.LoadScript(`syntaxError = SyntaxError; eval('() => new.target')`), syntaxError, "'new.target' is invalid in arrow nested in eval at global scope", "Invalid use of the 'new.target' keyword");
  228. assert.throws(() => WScript.LoadScript(`syntaxError = SyntaxError; (() => eval('new.target'))()`), syntaxError, "'new.target' is invalid in eval nested in arrow at global scope", "Invalid use of the 'new.target' keyword");
  229. }
  230. },
  231. {
  232. name: "Simple captures of new.target within ordinary functions",
  233. body: function () {
  234. function foo() {
  235. return new.target;
  236. }
  237. assert.areEqual(foo, new foo(), "Simple function returning new.target binding");
  238. assert.areEqual(undefined, foo(), "Simple function binds new.target to undefined without new keyword");
  239. function foo2() {
  240. var a = () => new.target;
  241. return a();
  242. }
  243. assert.areEqual(foo2, new foo2(), "Function returning new.target captured by nested arrow");
  244. assert.areEqual(undefined, foo2(), "Function with arrow capturing new.target binds new.target to undefined without new keyword");
  245. function foo3() {
  246. return eval('new.target');
  247. }
  248. assert.areEqual(foo3, new foo3(), "Function returning new.target captured by nested eval");
  249. assert.areEqual(undefined, foo3(), "Function with eval capturing new.target binds new.target to undefined without new keyword");
  250. function foo4() {
  251. var a = () => () => new.target;
  252. return a()();
  253. }
  254. assert.areEqual(foo4, new foo4(), "Function returning new.target captured by nested arrows");
  255. assert.areEqual(undefined, foo4(), "Function with nested arrows capturing new.target binds new.target to undefined without new keyword");
  256. function foo5() {
  257. return eval("eval('new.target');");
  258. }
  259. assert.areEqual(foo5, new foo5(), "Function returning new.target captured by nested evals");
  260. assert.areEqual(undefined, foo5(), "Function with nested evals capturing new.target binds new.target to undefined without new keyword");
  261. function foo6() {
  262. var a = () => eval('new.target');
  263. return a();
  264. }
  265. assert.areEqual(foo6, new foo6(), "Function returning new.target captured by arrow captured by eval");
  266. assert.areEqual(undefined, foo6(), "Function with nested evals capturing new.target binds new.target to undefined without new keyword");
  267. }
  268. },
  269. {
  270. name: "Base class empty constructor symbol binding",
  271. body: function() {
  272. class BaseDefault { }
  273. assert.areEqual(BaseDefault, new BaseDefault().constructor, "Base class with default constructor");
  274. eval(`
  275. class BaseEvalDefault { }
  276. assert.areEqual(BaseEvalDefault, new BaseEvalDefault().constructor, "Base class defined in eval with default constructor");
  277. `);
  278. class BaseEmpty {
  279. constructor() { }
  280. }
  281. assert.areEqual(BaseEmpty, new BaseEmpty().constructor, "Base class with empty constructor");
  282. eval(`
  283. class BaseEvalEmpty {
  284. constructor() { }
  285. }
  286. assert.areEqual(BaseEvalEmpty, new BaseEvalEmpty().constructor, "Base class defined in eval with empty constructor");
  287. `);
  288. }
  289. },
  290. {
  291. name: "Base class 'this' binding",
  292. body: function() {
  293. class BaseReturnThis {
  294. constructor() {
  295. return this;
  296. }
  297. }
  298. assert.areEqual(BaseReturnThis, new BaseReturnThis().constructor, "Base class with constructor that returns this");
  299. class BaseReturnThisEval {
  300. constructor() {
  301. return eval('this');
  302. }
  303. }
  304. assert.areEqual(BaseReturnThisEval, new BaseReturnThisEval().constructor, "Base class with constructor that returns this captured by eval");
  305. class BaseReturnThisArrow {
  306. constructor() {
  307. return (() => this)();
  308. }
  309. }
  310. assert.areEqual(BaseReturnThisArrow, new BaseReturnThisArrow().constructor, "Base class with constructor that returns this captured by arrow");
  311. eval(`
  312. class BaseEvalReturnThis {
  313. constructor() {
  314. return this;
  315. }
  316. }
  317. assert.areEqual(BaseEvalReturnThis, new BaseEvalReturnThis().constructor, "Base class defined in eval with constructor that returns this");
  318. `);
  319. eval(`
  320. class BaseEvalReturnThisEval {
  321. constructor() {
  322. return eval('this');
  323. }
  324. }
  325. assert.areEqual(BaseEvalReturnThisEval, new BaseEvalReturnThisEval().constructor, "Base class defined in eval with constructor that returns this captured via eval");
  326. `);
  327. eval(`
  328. class BaseEvalReturnThisLambda {
  329. constructor() {
  330. return (() => this)();
  331. }
  332. }
  333. assert.areEqual(BaseEvalReturnThisLambda, new BaseEvalReturnThisLambda().constructor, "Base class defined in eval with constructor that returns this captured via arrow");
  334. `);
  335. eval(`
  336. class BaseEvalReturnThisEvalLambda {
  337. constructor() {
  338. return eval('(() => this)()');
  339. }
  340. }
  341. assert.areEqual(BaseEvalReturnThisEvalLambda, new BaseEvalReturnThisEvalLambda().constructor, "Base class defined in eval with constructor that returns this captured via arrow inside eval");
  342. `);
  343. eval(`
  344. class BaseEvalReturnThisLambdaEval {
  345. constructor() {
  346. return (() => eval('this'))();
  347. }
  348. }
  349. assert.areEqual(BaseEvalReturnThisLambdaEval, new BaseEvalReturnThisLambdaEval().constructor, "Base class defined in eval with constructor that returns this captured via eval inside arrow");
  350. `);
  351. var called = false;
  352. class BaseVerifyThis {
  353. constructor() {
  354. assert.areEqual(BaseVerifyThis, this.constructor, "Base class this binding is an instance of the class");
  355. called = true;
  356. }
  357. }
  358. assert.areEqual(BaseVerifyThis, new BaseVerifyThis().constructor, "Base class constructor uses this object");
  359. assert.isTrue(called, "Constructor was actually called");
  360. called = false;
  361. class BaseVerifyThisEval {
  362. constructor() {
  363. eval(`
  364. assert.areEqual(BaseVerifyThisEval, this.constructor, "Base class this binding is an instance of the class");
  365. called = true;
  366. `);
  367. }
  368. }
  369. assert.areEqual(BaseVerifyThisEval, new BaseVerifyThisEval().constructor, "Base class constructor uses this object inside eval");
  370. assert.isTrue(called, "Constructor was actually called");
  371. called = false;
  372. class BaseVerifyThisArrow {
  373. constructor() {
  374. assert.areEqual(BaseVerifyThisArrow, (() => this)().constructor, "Base class this binding is an instance of the class");
  375. called = true;
  376. }
  377. }
  378. assert.areEqual(BaseVerifyThisArrow, new BaseVerifyThisArrow().constructor, "Base class constructor uses this object inside arrow");
  379. assert.isTrue(called, "Constructor was actually called");
  380. called = false;
  381. class BaseVerifyThisEvalArrow {
  382. constructor() {
  383. assert.areEqual(BaseVerifyThisEvalArrow, eval('(() => this)()').constructor, "Base class this binding is an instance of the class");
  384. called = true;
  385. }
  386. }
  387. assert.areEqual(BaseVerifyThisEvalArrow, new BaseVerifyThisEvalArrow().constructor, "Base class constructor uses this object inside arrow inside eval");
  388. assert.isTrue(called, "Constructor was actually called");
  389. called = false;
  390. class BaseVerifyThisArrowEval {
  391. constructor() {
  392. assert.areEqual(BaseVerifyThisArrowEval, (() => eval('this'))().constructor, "Base class this binding is an instance of the class");
  393. called = true;
  394. }
  395. }
  396. assert.areEqual(BaseVerifyThisArrowEval, new BaseVerifyThisArrowEval().constructor, "Base class constructor uses this object inside eval inside arrow");
  397. assert.isTrue(called, "Constructor was actually called");
  398. }
  399. },
  400. {
  401. name: "Base class new.target binding",
  402. body: function() {
  403. var called = false;
  404. class BaseVerifyNewTarget {
  405. constructor() {
  406. assert.areEqual(BaseVerifyNewTarget, new.target, "Base class called as new expression gets new.target");
  407. called = true;
  408. }
  409. }
  410. assert.areEqual(BaseVerifyNewTarget, new BaseVerifyNewTarget().constructor, "Base class constructor uses new.target");
  411. assert.isTrue(called, "Constructor was actually called");
  412. called = false;
  413. class BaseVerifyNewTargetEval {
  414. constructor() {
  415. eval(`
  416. assert.areEqual(BaseVerifyNewTargetEval, new.target, "Base class called as new expression gets new.target");
  417. called = true;
  418. `);
  419. }
  420. }
  421. assert.areEqual(BaseVerifyNewTargetEval, new BaseVerifyNewTargetEval().constructor, "Base class constructor uses new.target inside eval");
  422. assert.isTrue(called, "Constructor was actually called");
  423. called = false;
  424. class BaseVerifyNewTargetLambda {
  425. constructor() {
  426. (() => {
  427. assert.areEqual(BaseVerifyNewTargetLambda, new.target, "Base class called as new expression gets new.target");
  428. called = true;
  429. })();
  430. }
  431. }
  432. assert.areEqual(BaseVerifyNewTargetLambda, new BaseVerifyNewTargetLambda().constructor, "Base class constructor uses new.target inside lambda");
  433. assert.isTrue(called, "Constructor was actually called");
  434. called = false;
  435. class BaseVerifyNewTargetEvalLambda {
  436. constructor() {
  437. eval(`
  438. (() => {
  439. assert.areEqual(BaseVerifyNewTargetEvalLambda, new.target, "Base class called as new expression gets new.target");
  440. called = true;
  441. })();
  442. `);
  443. }
  444. }
  445. assert.areEqual(BaseVerifyNewTargetEvalLambda, new BaseVerifyNewTargetEvalLambda().constructor, "Base class constructor uses new.target inside lambda inside eval");
  446. assert.isTrue(called, "Constructor was actually called");
  447. called = false;
  448. class BaseVerifyNewTargetLambdaEval {
  449. constructor() {
  450. (() => {
  451. eval(`
  452. assert.areEqual(BaseVerifyNewTargetLambdaEval, new.target, "Base class called as new expression gets new.target");
  453. called = true;
  454. `);
  455. })();
  456. }
  457. }
  458. assert.areEqual(BaseVerifyNewTargetLambdaEval, new BaseVerifyNewTargetLambdaEval().constructor, "Base class constructor uses new.target inside eval inside lambda");
  459. assert.isTrue(called, "Constructor was actually called");
  460. called = false;
  461. eval(`
  462. class BaseEvalVerifyNewTarget {
  463. constructor() {
  464. assert.areEqual(BaseEvalVerifyNewTarget, new.target, "Base class called as new expression gets new.target");
  465. called = true;
  466. }
  467. }
  468. assert.areEqual(BaseEvalVerifyNewTarget, new BaseEvalVerifyNewTarget().constructor, "Base class defined in eval constructor uses new.target");
  469. assert.isTrue(called, "Constructor was actually called");
  470. `);
  471. }
  472. },
  473. {
  474. name: "Base class super call is syntax error",
  475. body: function() {
  476. var syntaxError;
  477. assert.throws(() => WScript.LoadScript(`syntaxError = SyntaxError; class Base { constructor() { super(); } }`), syntaxError, "'super' call is invalid syntax in a base class", "Invalid use of the 'super' keyword");
  478. assert.throws(() => WScript.LoadScript(`syntaxError = SyntaxError; class Base { constructor() { eval("super();"); } }; new Base();`), syntaxError, "'super' call is invalid syntax in a base class", "Invalid use of the 'super' keyword");
  479. assert.throws(() => WScript.LoadScript(`syntaxError = SyntaxError; class Base { constructor() { () => super(); } }`), syntaxError, "'super' call is invalid syntax in a base class", "Invalid use of the 'super' keyword");
  480. }
  481. },
  482. {
  483. name: "Class derived from null special symbol binding",
  484. body: function() {
  485. class DerivedDefault extends null { }
  486. assert.throws(() => new DerivedDefault(), TypeError, "Class derived from null can't be instantiated", "Function is not a constructor");
  487. class DerivedSuper extends null {
  488. constructor() {
  489. super();
  490. }
  491. }
  492. assert.throws(() => new DerivedSuper(), TypeError, "Class derived from null can't make a super call", "Function is not a constructor");
  493. }
  494. },
  495. {
  496. name: "Derived class 'this' binding",
  497. body: function() {
  498. class Base { }
  499. class DerivedReturnThis extends Base {
  500. constructor() {
  501. super();
  502. return this;
  503. }
  504. }
  505. assert.areEqual(DerivedReturnThis, new DerivedReturnThis().constructor, "Derived class with constructor that returns this");
  506. class DerivedReturnThisEval extends Base {
  507. constructor() {
  508. super();
  509. return eval('this');
  510. }
  511. }
  512. assert.areEqual(DerivedReturnThisEval, new DerivedReturnThisEval().constructor, "Derived class with constructor that returns this captured by eval");
  513. class DerivedReturnThisArrow extends Base {
  514. constructor() {
  515. super();
  516. return (() => this)();
  517. }
  518. }
  519. assert.areEqual(DerivedReturnThisArrow, new DerivedReturnThisArrow().constructor, "Derived class with constructor that returns this captured by arrow");
  520. class DerivedReturnThisEvalArrow extends Base {
  521. constructor() {
  522. super();
  523. return eval('(() => this)()');
  524. }
  525. }
  526. assert.areEqual(DerivedReturnThisEvalArrow, new DerivedReturnThisEvalArrow().constructor, "Derived class with constructor that returns this captured by arrow inside eval");
  527. class DerivedReturnThisArrowEval extends Base {
  528. constructor() {
  529. super();
  530. return (() => eval('this'))();
  531. }
  532. }
  533. assert.areEqual(DerivedReturnThisArrowEval, new DerivedReturnThisArrowEval().constructor, "Derived class with constructor that returns this captured by eval inside arrow");
  534. eval(`
  535. class DerivedEvalReturnThis extends Base {
  536. constructor() {
  537. super();
  538. return this;
  539. }
  540. }
  541. assert.areEqual(DerivedEvalReturnThis, new DerivedEvalReturnThis().constructor, "Derived class defined in eval with constructor that returns this");
  542. `);
  543. eval(`
  544. class DerivedEvalReturnThisEval extends Base {
  545. constructor() {
  546. super();
  547. return eval('this');
  548. }
  549. }
  550. assert.areEqual(DerivedEvalReturnThisEval, new DerivedEvalReturnThisEval().constructor, "Derived class defined in eval with constructor that returns this captured by eval");
  551. `);
  552. eval(`
  553. class DerivedEvalReturnThisLambda extends Base {
  554. constructor() {
  555. super();
  556. return (() => this)();
  557. }
  558. }
  559. assert.areEqual(DerivedEvalReturnThisLambda, new DerivedEvalReturnThisLambda().constructor, "Derived class defined in eval with constructor that returns this captured by arrow");
  560. `);
  561. }
  562. },
  563. {
  564. name: "Derived class verifying 'this' binding",
  565. body: function() {
  566. class Base { }
  567. var called = false;
  568. class DerivedVerifyThis extends Base {
  569. constructor() {
  570. super();
  571. assert.areEqual(DerivedVerifyThis, this.constructor, "Derived class with constructor that verifies this");
  572. called = true;
  573. }
  574. }
  575. assert.areEqual(DerivedVerifyThis, new DerivedVerifyThis().constructor, "Derived class with constructor that verifies this");
  576. assert.isTrue(called, "Constructor was called");
  577. called = false;
  578. class DerivedVerifyThisLambda extends Base {
  579. constructor() {
  580. super();
  581. (() => {
  582. assert.areEqual(DerivedVerifyThisLambda, this.constructor, "Derived class with constructor that verifies this captured in lambda");
  583. called = true;
  584. })();
  585. }
  586. }
  587. assert.areEqual(DerivedVerifyThisLambda, new DerivedVerifyThisLambda().constructor, "Derived class with constructor that verifies this captured by arrow");
  588. assert.isTrue(called, "Constructor was called");
  589. eval(`
  590. called = false;
  591. class DerivedEvalVerifyThis extends Base {
  592. constructor() {
  593. super();
  594. assert.areEqual(DerivedEvalVerifyThis, this.constructor, "Derived class defined in eval with constructor that verifies this");
  595. called = true;
  596. }
  597. }
  598. assert.areEqual(DerivedEvalVerifyThis, new DerivedEvalVerifyThis().constructor, "Derived class defined in eval with constructor that verifies this");
  599. assert.isTrue(called, "Constructor was called");
  600. `);
  601. }
  602. },
  603. {
  604. name: "Assignment to special symbols is invalid",
  605. body: function() {
  606. function test(code, message) {
  607. assert.throws(() => WScript.LoadScript(code), SyntaxError, message, "Invalid left-hand side in assignment.");
  608. }
  609. function testglobal(symname) {
  610. test(`${symname} = 'something'`, `Assignment to global '${symname}'`);
  611. test(`(() => ${symname} = 'something')()`, `Assignment to global '${symname}' captured by lambda`);
  612. test(`eval("${symname} = 'something'")`, `Assignment to global '${symname}' in eval`);
  613. test(`(() => eval("${symname} = 'something'"))()`, `Assignment to global '${symname}' captured by lambda in eval`);
  614. test(`eval("(() => ${symname} = 'something')()")`, `Assignment to global '${symname}' captured by eval in lambda`);
  615. }
  616. function testlocal(symname) {
  617. test(`function foo() { ${symname} = 'something'; }; foo();`, `Assignment to function '${symname}'`);
  618. test(`function foo() { eval("${symname} = 'something'"); }; foo();`, `Assignment to function '${symname}' in nested eval`);
  619. test(`function foo() { (() => ${symname} = 'something')(); }; foo();`, `Assignment to function '${symname}' in nested lambda`);
  620. }
  621. testglobal('this');
  622. testlocal('this');
  623. testlocal('new.target');
  624. }
  625. },
  626. {
  627. name: "Eval 'this' binding",
  628. body: function() {
  629. function SloppyModeCapture() {
  630. assert.areEqual('object', typeof this, "'this' object given to functions in sloppy mode");
  631. this.o = 'SloppyModeCapture';
  632. eval(`assert.areEqual('SloppyModeCapture', this.o, "Direct eval captures 'this' in sloppy mode")`);
  633. eval(`'use strict'; assert.areEqual('SloppyModeCapture', this.o, "Direct strict mode eval captures 'this' in sloppy mode")`);
  634. var not_eval = eval;
  635. not_eval(`assert.areEqual('SloppyModeCapture', this.o, "Indirect eval captures 'this' in sloppy mode")`);
  636. not_eval(`'use strict'; assert.areEqual('SloppyModeCapture', this.o, "Indirect strict mode eval captures 'this' in sloppy mode")`);
  637. }
  638. SloppyModeCapture();
  639. function StrictModeSanity() {
  640. 'use strict';
  641. assert.areEqual(undefined, this, "Strict mode function doesn't get a 'this' object");
  642. }
  643. StrictModeSanity();
  644. lambdaForGlobalThis().o = 'global';
  645. function StrictModeCapture() {
  646. 'use strict';
  647. assert.areEqual('object', typeof this, "'this' object should be passed-in");
  648. eval(`assert.areEqual('StrictModeCapture', this.o, "Direct eval captures 'this' in strict mode")`);
  649. eval(`'use strict'; assert.areEqual('StrictModeCapture', this.o, "Direct strict mode eval captures 'this' in strict mode")`);
  650. var not_eval = eval;
  651. not_eval(`assert.areEqual('global', this.o, "Indirect eval captures global 'this' in strict mode")`);
  652. not_eval(`'use strict'; assert.areEqual('global', this.o, "Indirect strict mode eval captures global 'this' in strict mode")`);
  653. }
  654. StrictModeCapture.call({o:'StrictModeCapture'});
  655. }
  656. },
  657. {
  658. name: "Super reference patterns",
  659. body: function() {
  660. var key_bar = 'bar';
  661. var key_foo = 'foo';
  662. function verifyThis() {
  663. assert.areEqual(obj, this, "'this' passed via super method call should be the object");
  664. return 'bar';
  665. }
  666. var obj = {
  667. callSuperMethodViaDot() { return super.bar(); },
  668. callSuperMethodViaDotArrow() { return (() => super.bar())(); },
  669. callSuperMethodViaDotEval() { return eval('super.bar();'); },
  670. getSuperPropertyViaDot() { return super.foo; },
  671. getSuperPropertyViaDotArrow() { return (() => super.foo)(); },
  672. getSuperPropertyViaDotEval() { return eval('super.foo;'); },
  673. callSuperMethodViaIndex() { return super[key_bar](); },
  674. callSuperMethodViaIndexArrow() { return (() => super[key_bar]())(); },
  675. callSuperMethodViaIndexEval() { return eval('super[key_bar]();'); },
  676. getSuperPropertyViaIndex() { return super[key_foo]; },
  677. getSuperPropertyViaIndexArrow() { return (() => super[key_foo])(); },
  678. getSuperPropertyViaIndexEval() { return eval('super[key_foo];'); },
  679. assignSuperPropertyViaDot() { super.foo = 'something'; return super.foo; },
  680. assignSuperPropertyViaDotArrow() { (() => super.foo = 'something')(); return super.foo; },
  681. assignSuperPropertyViaDotEval() { eval("super.foo = 'something';"); return super.foo; },
  682. assignSuperPropertyViaIndex() { super[key_foo] = 'something'; return super.foo; },
  683. assignSuperPropertyViaIndexArrow() { (() => super[key_foo] = 'something')(); return super.foo; },
  684. assignSuperPropertyViaIndexEval() { eval("super[key_foo] = 'something';"); return super.foo; },
  685. deleteSuperPropertyViaDot() { delete super.foo; },
  686. deleteSuperPropertyViaDotArrow() { (() => delete super.foo)(); },
  687. deleteSuperPropertyViaDotEval() { eval('delete super.foo;'); },
  688. }
  689. var proto = {
  690. bar: verifyThis,
  691. foo: 'foo',
  692. }
  693. Object.setPrototypeOf(obj, proto);
  694. assert.areEqual('bar', obj.callSuperMethodViaDot(), "Call method via 'super.method()'");
  695. assert.areEqual('bar', obj.callSuperMethodViaDotArrow(), "Call method via 'super.method()' in arrow");
  696. assert.areEqual('bar', obj.callSuperMethodViaDotEval(), "Call method via 'super.method()' in eval");
  697. assert.areEqual('foo', obj.getSuperPropertyViaDot(), "Get property via 'super.property'");
  698. assert.areEqual('foo', obj.getSuperPropertyViaDotArrow(), "Get property via 'super.property' in arrow");
  699. assert.areEqual('foo', obj.getSuperPropertyViaDotEval(), "Get property via 'super.property' in eval");
  700. assert.areEqual('bar', obj.callSuperMethodViaIndex(), "Call method via 'super[method]()'");
  701. assert.areEqual('bar', obj.callSuperMethodViaIndexArrow(), "Call method via 'super[method]()' in arrow");
  702. assert.areEqual('bar', obj.callSuperMethodViaIndexEval(), "Call method via 'super[method]()' in eval");
  703. assert.areEqual('foo', obj.getSuperPropertyViaIndex(), "Get property via 'super[property]'");
  704. assert.areEqual('foo', obj.getSuperPropertyViaIndexArrow(), "Get property via 'super[property]' in arrow");
  705. assert.areEqual('foo', obj.getSuperPropertyViaIndexEval(), "Get property via 'super[property]' in eval");
  706. assert.areEqual('foo', obj.assignSuperPropertyViaDot(), "Assign property via 'super.property'");
  707. assert.areEqual('foo', obj.assignSuperPropertyViaDotArrow(), "Assign property via 'super.property' in arrow");
  708. assert.areEqual('foo', obj.assignSuperPropertyViaDotEval(), "Assign property via 'super.property' in eval");
  709. assert.areEqual('something', obj.assignSuperPropertyViaIndex(), "Assign property via 'super[property]'");
  710. assert.areEqual('something', obj.assignSuperPropertyViaIndexArrow(), "Assign property via 'super[property]' in arrow");
  711. assert.areEqual('something', obj.assignSuperPropertyViaIndexEval(), "Assign property via 'super[property]' in eval");
  712. assert.throws(() => obj.deleteSuperPropertyViaDot(), ReferenceError, "Delete property via 'super.property'", "Unable to delete property with a super reference");
  713. assert.throws(() => obj.deleteSuperPropertyViaDotArrow(), ReferenceError, "Delete property via 'super.property' in arrow", "Unable to delete property with a super reference");
  714. assert.throws(() => obj.deleteSuperPropertyViaDotEval(), ReferenceError, "Delete property via 'super.property' in eval", "Unable to delete property with a super reference");
  715. }
  716. },
  717. {
  718. name: "Split-scope with eval in parameter scope",
  719. body: function() {
  720. function f1({a:a}, c = eval("a")) {
  721. return c;
  722. }
  723. assert.areEqual('ok', f1({a:'ok'}), "Eval references previous non-simple formal");
  724. function f2({a:a}, c = eval('this')) {
  725. return c;
  726. }
  727. var _this = {};
  728. assert.areEqual(_this, f2.call(_this, {}), "Eval references 'this' binding");
  729. function f3({a:a}, c = eval('new.target')) {
  730. return c;
  731. }
  732. assert.areEqual(f3, new f3({}), "Eval references 'new.target' binding");
  733. class base {
  734. prop() {
  735. assert.areEqual(c1, this.constructor, "Be sure the 'this' binding flows down the super reference calls")
  736. return 'prop';
  737. }
  738. returnThis() {
  739. assert.areEqual(c2, this.constructor, "Be sure the 'this' binding flows down the super reference calls")
  740. return this;
  741. }
  742. }
  743. var prop_name = 'prop';
  744. class c1 extends base {
  745. constructor({a:a}, c = eval('super()')) {
  746. assert.areEqual(c1, new.target, "'new.target' binding is correct in class constructor with split-scope");
  747. assert.areEqual(this, c, "Result of super() should be the same as 'this'");
  748. assert.areEqual('prop', super.prop(), "Super reference should bind correctly");
  749. }
  750. doSuperRefViaDot({a:a}, c = eval('super.prop()')) {
  751. return c;
  752. }
  753. doSuperRefViaIndex({a:a}, c = eval('super[prop_name]()')) {
  754. return c;
  755. }
  756. }
  757. var inst = new c1({});
  758. assert.areEqual(c1, inst.constructor, "Eval references 'super' in a constructor call");
  759. assert.areEqual('prop', inst.doSuperRefViaDot({}), "Eval references 'super' as a super property reference via dot");
  760. assert.areEqual('prop', inst.doSuperRefViaIndex({}), "Eval references 'super' as a super property reference via index");
  761. class c2 extends base {
  762. constructor({a:a}, c = eval('super(); super.returnThis();')) {
  763. assert.areEqual(c2, new.target, "'new.target' binding is correct in class constructor with split-scope");
  764. assert.areEqual(this, c, "Result of super.returnThis() should be the same as 'this'");
  765. }
  766. }
  767. var inst = new c2({});
  768. assert.areEqual(c2, inst.constructor, "Eval references 'super' in a constructor call");
  769. }
  770. },
  771. {
  772. name: "Formal argument named 'arguments'",
  773. body: function() {
  774. function foo(arguments) {
  775. assert.areEqual('arguments', arguments, "Arguments named formal accessed directly");
  776. assert.areEqual('arguments', (() => arguments)(), "Arguments named formal accessed through a lambda capture");
  777. assert.areEqual('arguments', eval('arguments'), "Arguments named formal accessed through an eval");
  778. }
  779. foo('arguments');
  780. }
  781. },
  782. {
  783. name: "Global 'this' binding captured by strict-mode arrow",
  784. body: function() {
  785. WScript.LoadScript(`"use strict";
  786. assert.areEqual(this, (() => this)(), "Lambda should load the global 'this' value itself via LdThis (not StrictLdThis)");
  787. `);
  788. WScript.LoadScript(`
  789. assert.areEqual(this, (() => { "use strict"; return this; })(), "Lambda which has a 'use strict' clause inside");
  790. `);
  791. WScript.LoadScript(`"use strict";
  792. assert.areEqual('object', typeof (() => this)(), "Verify lambda can load global 'this' value even if the global body itself does not have a 'this' binding");
  793. `);
  794. }
  795. },
  796. {
  797. name: "PidRefStack might be out-of-order when we try to add special symbol var decl",
  798. body: function() {
  799. // Failure causes an assert to fire
  800. WScript.LoadScript(`(a = function() { this }, b = (this)) => {}`);
  801. assert.throws(() => WScript.LoadScript(`[ a = function () { this; } ((this)) = 1 ] = []`), ReferenceError, "Not a valid destructuring assignment but should not fire assert", "Invalid left-hand side in assignment");
  802. }
  803. },
  804. {
  805. name: "Non-split scope with default arguments referencing special names",
  806. body: function() {
  807. var _this = {}
  808. function foo(a = this) {
  809. eval('');
  810. assert.areEqual(_this, a, "Correct default value was assigned");
  811. assert.areEqual(_this, this, "Regular 'this' binding is correct");
  812. }
  813. foo.call(_this)
  814. function bar(a = this) {
  815. eval('');
  816. assert.areEqual(_this, a, "Correct default value was assigned");
  817. assert.areEqual(_this, this, "Regular 'this' binding is correct");
  818. function b() { return 'b'; }
  819. assert.areEqual('b', b(), "Nested functions are bound to the correct slot");
  820. }
  821. bar.call(_this)
  822. function baz(a = this, c = function() { return 'c'; }) {
  823. eval('');
  824. assert.areEqual(_this, a, "Correct default value was assigned");
  825. assert.areEqual(_this, this, "Regular 'this' binding is correct");
  826. function b() { return 'b'; }
  827. assert.areEqual('b', b(), "Nested functions are bound to the correct slot");
  828. assert.areEqual('c', c(), "Function expression in param scope default argument is bound to the correct slot");
  829. }
  830. baz.call(_this)
  831. function baz2(a = this, c = function() { function nested() { return 'c' }; return nested; }) {
  832. eval('');
  833. assert.areEqual(_this, a, "Correct default value was assigned");
  834. assert.areEqual(_this, this, "Regular 'this' binding is correct");
  835. function b() { return 'b'; }
  836. assert.areEqual('b', b(), "Nested functions are bound to the correct slot");
  837. assert.areEqual('c', c()(), "Function decl nested in function expression assigned to default argument");
  838. }
  839. baz2.call(_this)
  840. function baz3(c = function() { return 'c'; }, a = this) {
  841. eval('');
  842. assert.areEqual(_this, a, "Correct default value was assigned");
  843. assert.areEqual(_this, this, "Regular 'this' binding is correct");
  844. function b() { return 'b'; }
  845. assert.areEqual('b', b(), "Nested functions are bound to the correct slot");
  846. assert.areEqual('c', c(), "Function expression in param scope default argument is bound to the correct slot");
  847. }
  848. baz3.call(_this)
  849. function bat(a = this, c = () => 'c') {
  850. eval('');
  851. assert.areEqual(_this, a, "Correct default value was assigned");
  852. assert.areEqual(_this, this, "Regular 'this' binding is correct");
  853. function b() { return 'b'; }
  854. assert.areEqual('b', b(), "Nested functions are bound to the correct slot");
  855. assert.areEqual('c', c(), "Lambda expression in param scope default argument is bound to the correct slot");
  856. }
  857. bat.call(_this)
  858. function bat2(a = this, c = () => () => 'c') {
  859. eval('');
  860. assert.areEqual(_this, a, "Correct default value was assigned");
  861. assert.areEqual(_this, this, "Regular 'this' binding is correct");
  862. function b() { return 'b'; }
  863. assert.areEqual('b', b(), "Nested functions are bound to the correct slot");
  864. assert.areEqual('c', c()(), "Lambda function decl nested in lambda expression assigned to default argument");
  865. }
  866. bat2.call(_this)
  867. function bat3(c = () => () => 'c', a = this) {
  868. eval('');
  869. assert.areEqual(_this, a, "Correct default value was assigned");
  870. assert.areEqual(_this, this, "Regular 'this' binding is correct");
  871. function b() { return 'b'; }
  872. assert.areEqual('b', b(), "Nested functions are bound to the correct slot");
  873. assert.areEqual('c', c()(), "Lambda function decl nested in lambda expression assigned to default argument");
  874. }
  875. bat3.call(_this)
  876. }
  877. },
  878. {
  879. name: "Loading 'this' binding as a call target",
  880. body: function() {
  881. assert.throws(() => WScript.LoadScript(`with({}) { this() }`), TypeError, "Loading global 'this' binding as a call target should always throw - 'this' is an object", "Function expected");
  882. assert.throws(() => this(), TypeError, "Capturing function 'this' binding and emitting as a call target should throw if 'this' is not a function", "Function expected");
  883. }
  884. },
  885. {
  886. name: "Class expression as call target without 'this' binding",
  887. body: function() {
  888. assert.throws(() => WScript.LoadScript(`(class classExpr {}())`), TypeError, "Class expression called at global scope", "Class constructor cannot be called without the new keyword");
  889. assert.throws(() => WScript.LoadScript(`(() => (class classExpr {}()))()`), TypeError, "Class expression called in global lambda", "Class constructor cannot be called without the new keyword");
  890. }
  891. },
  892. {
  893. name: "Indirect eval should not create a 'this' binding",
  894. body: function() {
  895. WScript.LoadScript(`
  896. this.eval("(() => assert.areEqual('global', this.o, 'Lambda in indirect eval called off of this capturing this'))()");
  897. this['eval']("(() => assert.areEqual('global', this.o, 'Lambda in indirect eval called from a property index capturing this'))()");
  898. var _eval = 'eval';
  899. this[_eval]("(() => assert.areEqual('global', this.o, 'Lambda in indirect eval called from a property index capturing this'))()");
  900. _eval = eval;
  901. _eval("(() => assert.areEqual('global', this.o, 'Lambda in indirect eval capturing this'))()");
  902. `);
  903. }
  904. }
  905. ]
  906. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });