classes.js 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197
  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. function verifyClassMember(obj, name, expectedReturnValue, isGet, isSet, isGenerator) {
  7. let p = Object.getOwnPropertyDescriptor(obj, name);
  8. let strMethodSignature = `obj[${name}](${isGet},${isSet},${isGenerator})`;
  9. let fn;
  10. if (isGet) {
  11. fn = p.get;
  12. assert.areEqual('function', typeof fn, `${strMethodSignature}: Get method has 'get' property set in descriptor`);
  13. assert.areEqual(expectedReturnValue, obj[name], `${strMethodSignature}: Invoking class get method returns correct value`);
  14. assert.areEqual(expectedReturnValue, fn(), `${strMethodSignature}: Calling class get method function directly returns correct value`);
  15. assert.isFalse('prototype' in fn, `${strMethodSignature}: Class method get does not have 'prototype' property`);
  16. } else if (isSet) {
  17. fn = p.set;
  18. assert.areEqual('function', typeof fn, `${strMethodSignature}: Set method has 'set' property set in descriptor`);
  19. assert.areEqual(undefined, obj[name], `${strMethodSignature}: Invoking class set method returns undefined`);
  20. assert.areEqual(expectedReturnValue, fn(), `${strMethodSignature}: Calling class set method function directly returns correct value`);
  21. assert.isFalse('prototype' in fn, `${strMethodSignature}: Class method set does not have 'prototype' property`);
  22. } else if (isGenerator) {
  23. fn = obj[name];
  24. assert.areEqual('function', typeof fn, `${strMethodSignature}: Class method generator function has correct type`);
  25. let s;
  26. for (s of fn()) {}
  27. assert.areEqual(expectedReturnValue, s, `${strMethodSignature}: Calling class method generator returns correct value`);
  28. assert.isTrue(p.writable, `${strMethodSignature}: Class method generator functions are writable`);
  29. assert.isTrue('prototype' in fn, `${strMethodSignature}: Class method generator does have 'prototype' property`);
  30. } else {
  31. fn = obj[name]
  32. assert.areEqual('function', typeof fn, `${strMethodSignature}: Class method has correct type`);
  33. assert.areEqual(expectedReturnValue, fn(), `${strMethodSignature}: Calling class method returns correct value`);
  34. // get/set property descriptors do not have writable properties
  35. assert.isTrue(p.writable, `${strMethodSignature}: Class method functions are writable`);
  36. assert.isFalse('prototype' in fn, `${strMethodSignature}: Class method does not have 'prototype' property`);
  37. }
  38. assert.isFalse(p.enumerable, `${strMethodSignature}: Class methods are not enumerable`);
  39. assert.isTrue(p.configurable, `${strMethodSignature}: Class methods are configurable`);
  40. assert.isFalse(fn.hasOwnProperty('arguments'), `${strMethodSignature}: Class methods do not have an 'arguments' property`);
  41. assert.isFalse(fn.hasOwnProperty('caller'), `${strMethodSignature}: Class methods do not have an 'caller' property`);
  42. }
  43. var tests = [
  44. {
  45. name: "Class requires an extends expression if the extends keyword is used",
  46. body: function () {
  47. assert.throws(function () { eval("class E extends { }") }, SyntaxError);
  48. }
  49. },
  50. {
  51. name: "Class declarations require a name",
  52. body: function () {
  53. assert.throws(function () { eval("class { }") }, SyntaxError);
  54. }
  55. },
  56. {
  57. name: "Class methods may not have an octal name",
  58. body: function () {
  59. assert.throws(function () { eval("class E0 { 0123() {} }") }, SyntaxError, "0123");
  60. assert.throws(function () { eval("class E1 { 0123.1() {} }") }, SyntaxError, "0123.1");
  61. }
  62. },
  63. {
  64. name: "Class prototypes must be non-writable",
  65. body: function () {
  66. var d = Object.getOwnPropertyDescriptor(class { }, "prototype");
  67. assert.isFalse(d.writable);
  68. }
  69. },
  70. {
  71. name: "Class static methods may not be named 'prototype'",
  72. body: function () {
  73. assert.throws(function () { eval("class E0 { static prototype() {} }") }, SyntaxError, "static prototype");
  74. assert.throws(function () { eval("class E1 { static get prototype() {} }") }, SyntaxError, "static get prototype");
  75. assert.throws(function () { eval("class E2 { static set prototype(x) {} }") }, SyntaxError, "static set prototype");
  76. }
  77. },
  78. {
  79. name: "Class constructor method can only be a normal method - not getter, setter, or generator",
  80. body: function () {
  81. assert.throws(function () { eval("class E { * constructor() {} }") }, SyntaxError, "Class constructor may not be a generator");
  82. assert.throws(function () { eval("class E0 { get constructor() {} }") }, SyntaxError, "get constructor");
  83. assert.throws(function () { eval("class E1 { set constructor(x) {} }") }, SyntaxError, "set constructor");
  84. }
  85. },
  86. {
  87. name: "Class method names can be duplicated; last one lexically always win",
  88. body: function () {
  89. assert.throws(function () { eval("class E0 { constructor() {} constructor() {} }") }, SyntaxError, "Duplicated constructor");
  90. // Valid
  91. class A { foo() {} foo() {} }
  92. class B { get foo() {} get foo() {} }
  93. class C { set foo(x) {} set foo(x) {} }
  94. class D { get foo() {} set foo(x) {} }
  95. class E { static foo() {} static foo() {} }
  96. class F { static get foo() {} static get foo() {} }
  97. class G { static set foo(x) {} static set foo(x) {} }
  98. class H { static get foo() {} static set foo(x) {} }
  99. class I { foo() {} get foo() {} set foo(x) {}}
  100. class J { static get foo() {} static set foo(x) {} get foo() {} set foo(x) {} }
  101. class K { static foo() {} static get foo() {} static set foo(x) {}}
  102. class L { static foo() {} foo() {} }
  103. class M { static foo() {} get foo() {} set foo(x) {}}
  104. class N { foo() {} static get foo() {} static set foo(x) {}}
  105. }
  106. },
  107. {
  108. name: "Class extends expressions must be (null || an object that is a constructor with a prototype that is (null || an object))",
  109. body: function () {
  110. class BaseClass {}
  111. assert.isTrue(Object.getPrototypeOf(BaseClass.prototype) === Object.prototype, "Object.getPrototypeOf(BaseClass.prototype) === Object.prototype")
  112. assert.isTrue(Object.getPrototypeOf(BaseClass.prototype.constructor) === Function.prototype, "Object.getPrototypeOf(BaseClass.prototype.constructor) === Function.prototype")
  113. class ExtendsNull extends null { }
  114. assert.isTrue(Object.getPrototypeOf(ExtendsNull.prototype) === null, "Object.getPrototypeOf(ExtendsNull.prototype) === null")
  115. assert.isTrue(Object.getPrototypeOf(ExtendsNull.prototype.constructor) === Function.prototype, "Object.getPrototypeOf(ExtendsNull.prototype.constructor) === Function.prototype")
  116. function NullPrototype () {}
  117. NullPrototype.prototype = null;
  118. class ExtendsNullPrototype extends NullPrototype {}
  119. assert.isTrue(Object.getPrototypeOf(ExtendsNullPrototype.prototype) === null, "Object.getPrototypeOf(ExtendsNullPrototype.prototype) === null")
  120. assert.isTrue(Object.getPrototypeOf(ExtendsNullPrototype.prototype.constructor) === NullPrototype, "Object.getPrototypeOf(ExtendsNullPrototype.prototype.constructor) === NullPrototype")
  121. class ExtendsObject extends Object {}
  122. assert.isTrue(Object.getPrototypeOf(ExtendsObject.prototype) === Object.prototype, "Object.getPrototypeOf(ExtendsObject.prototype) === Object.prototype")
  123. assert.isTrue(Object.getPrototypeOf(ExtendsObject.prototype.constructor) === Object, "Object.getPrototypeOf(ExtendsObject.prototype.constructor) === Object")
  124. function Func () {}
  125. class ExtendsFunc extends Func {}
  126. assert.isTrue(Object.getPrototypeOf(ExtendsFunc.prototype) === Func.prototype, "Object.getPrototypeOf(ExtendsFunc.prototype) === Func.prototype")
  127. assert.isTrue(Object.getPrototypeOf(ExtendsFunc.prototype.constructor) === Func, "Object.getPrototypeOf(ExtendsFunc.prototype.constructor) === Func")
  128. assert.throws(function () { class A extends 0 { } }, TypeError, "Integer extends");
  129. assert.throws(function () { class A extends "test" { } }, TypeError, "String extends");
  130. assert.throws(function () { class A extends {} { } }, TypeError, "Object literal extends");
  131. assert.throws(function () { class A extends undefined { } }, TypeError, "Undefined extends");
  132. assert.throws(
  133. function () {
  134. function Foo() {}
  135. Foo.prototype = 0;
  136. class A extends Foo { }
  137. }, TypeError, "Integer prototype");
  138. assert.throws(
  139. function () {
  140. function Foo() {}
  141. Foo.prototype = "test";
  142. class A extends Foo { }
  143. }, TypeError, "String prototype");
  144. assert.throws(
  145. function () {
  146. function Foo() {}
  147. Foo.prototype = undefined;
  148. class A extends Foo { }
  149. }, TypeError, "Undefined prototype");
  150. assert.doesNotThrow(function () { eval("class Foo extends new Proxy(class Bar {},{}){}"); });
  151. }
  152. },
  153. {
  154. name: "Class basic sanity tests",
  155. body: function () {
  156. var m;
  157. function p(x) {
  158. m = x;
  159. }
  160. class Empty { }
  161. class EmptySemi { ; }
  162. class OnlyCtor { constructor() { p('ctor') } }
  163. class OnlyMethod { method() { p('method') } }
  164. class OnlyStaticMethod { static method() { p('smethod') } }
  165. class OnlyGetter { get getter() { p('getter') } }
  166. class OnlyStaticGetter { static get getter() { p('sgetter') } }
  167. class OnlySetter { set setter(x) { p('setter ' + x) } }
  168. class OnlyStaticSetter { static set setter(x) { p('ssetter ' + x) } }
  169. let empty = new Empty();
  170. let emptySemi = new EmptySemi();
  171. let onlyCtor = new OnlyCtor();
  172. assert.areEqual('ctor', m, "constructing OnlyCtor class calls the constructor method");
  173. let onlyMethod = new OnlyMethod();
  174. let onlyStaticMethod = new OnlyStaticMethod();
  175. let onlyGetter = new OnlyGetter();
  176. let onlyStaticGetter = new OnlyStaticGetter();
  177. let onlySetter = new OnlySetter();
  178. let onlyStaticSetter = new OnlyStaticSetter();
  179. onlyMethod.method();
  180. assert.areEqual('method', m, "method calls function correctly");
  181. OnlyStaticMethod.method();
  182. assert.areEqual('smethod', m, "static method calls function correctly");
  183. onlyGetter.getter;
  184. assert.areEqual('getter', m, "getter calls function correctly");
  185. OnlyStaticGetter.getter;
  186. assert.areEqual('sgetter', m, "static getter calls function correctly");
  187. onlySetter.setter = null;
  188. assert.areEqual('setter null', m, "setter calls function correctly");
  189. OnlyStaticSetter.setter = null;
  190. assert.areEqual('ssetter null', m, "static setter calls function correctly");
  191. class InheritMethod extends OnlyMethod { method2() { p('sub method') } }
  192. class OverrideMethod extends OnlyMethod { method() { p('override method') } }
  193. let inheritMethod = new InheritMethod();
  194. let overrideMethod = new OverrideMethod();
  195. inheritMethod.method();
  196. assert.areEqual('method', m, "derived class inherits base class method");
  197. inheritMethod.method2();
  198. assert.areEqual('sub method', m, "derived class adds new method correctly");
  199. overrideMethod.method();
  200. assert.areEqual('override method', m, "derived class overrides method correctly");
  201. let OnlyMethodExpr = class OnlyMethodExpr { method() { p('class expr method') } }
  202. let OnlyMethodExprNameless = class { method() { p('class expr no name method') } }
  203. let onlyMethodExpr = new OnlyMethodExpr();
  204. let onlyMethodExprNameless = new OnlyMethodExprNameless();
  205. onlyMethodExpr.method();
  206. assert.areEqual('class expr method', m, "method call on class expression works correctly");
  207. onlyMethodExprNameless.method();
  208. assert.areEqual('class expr no name method', m, "method call on class expression with no name works correctly");
  209. class InternalNameUse { static method() { p(InternalNameUse.method.toString()) } }
  210. let InternalNameUseExpr_ = class InternalNameUseExpr { static method() { p(InternalNameUseExpr.method.toString()) } }
  211. InternalNameUse.method();
  212. assert.areEqual('method() { p(InternalNameUse.method.toString()) }', m, "Use of class declaration's name inside method works correctly");
  213. InternalNameUseExpr_.method();
  214. assert.areEqual('method() { p(InternalNameUseExpr.method.toString()) }', m, "Use of class expression's name inside method works correctly");
  215. }
  216. },
  217. {
  218. name: "Class basic sanity tests in closures",
  219. body: function () {
  220. var m;
  221. function p(x) {
  222. m = x;
  223. }
  224. function f1() {
  225. class Empty { }
  226. class EmptySemi { ; }
  227. class OnlyCtor { constructor() { p('ctor') } }
  228. class OnlyMethod { method() { p('method') } }
  229. class OnlyStaticMethod { static method() { p('smethod') } }
  230. class OnlyGetter { get getter() { p('getter') } }
  231. class OnlyStaticGetter { static get getter() { p('sgetter') } }
  232. class OnlySetter { set setter(x) { p('setter ' + x) } }
  233. class OnlyStaticSetter { static set setter(x) { p('ssetter ' + x) } }
  234. class OnlyComputedMethod { ["cmethod"]() { p('cmethod') } }
  235. class OnlyStaticComputedMethod { static ["cmethod"]() { p('scmethod') } }
  236. class OnlyComputedGetter { get ["cgetter"]() { p('cgetter') } }
  237. class OnlyStaticComputedGetter { static get ["cgetter"]() { p('scgetter') } }
  238. class OnlyComputedSetter { set ["csetter"](x) { p('csetter ' + x) } }
  239. class OnlyStaticComputedSetter { static set ["csetter"](x) { p('scsetter ' + x) } }
  240. function f2() {
  241. let empty = new Empty();
  242. let emptySemi = new EmptySemi();
  243. let onlyCtor = new OnlyCtor();
  244. assert.areEqual('ctor', m, "constructing OnlyCtor class calls the constructor method");
  245. let onlyMethod = new OnlyMethod();
  246. let onlyStaticMethod = new OnlyStaticMethod();
  247. let onlyGetter = new OnlyGetter();
  248. let onlyStaticGetter = new OnlyStaticGetter();
  249. let onlySetter = new OnlySetter();
  250. let onlyStaticSetter = new OnlyStaticSetter();
  251. let onlyComputedMethod = new OnlyComputedMethod();
  252. let onlyComputedGetter = new OnlyComputedGetter();
  253. let onlyComputedSetter = new OnlyComputedSetter();
  254. onlyMethod.method();
  255. assert.areEqual('method', m, "method calls function correctly");
  256. OnlyStaticMethod.method();
  257. assert.areEqual('smethod', m, "static method calls function correctly");
  258. onlyGetter.getter;
  259. assert.areEqual('getter', m, "getter calls function correctly");
  260. OnlyStaticGetter.getter;
  261. assert.areEqual('sgetter', m, "static getter calls function correctly");
  262. onlySetter.setter = null;
  263. assert.areEqual('setter null', m, "setter calls function correctly");
  264. OnlyStaticSetter.setter = null;
  265. assert.areEqual('ssetter null', m, "static setter calls function correctly");
  266. onlyComputedMethod.cmethod()
  267. assert.areEqual('cmethod', m, "computed name method calls function correctly");
  268. OnlyStaticComputedMethod.cmethod()
  269. assert.areEqual('scmethod', m, "static computed name method calls function correctly");
  270. onlyComputedGetter.cgetter;
  271. assert.areEqual('cgetter', m, "computed name getter calls function correctly");
  272. OnlyStaticComputedGetter.cgetter;
  273. assert.areEqual('scgetter', m, "static computed name getter calls function correctly");
  274. onlyComputedSetter.csetter = null;
  275. assert.areEqual('csetter null', m, "computed name setter calls function correctly");
  276. OnlyStaticComputedSetter.csetter = null;
  277. assert.areEqual('scsetter null', m, "static computed name setter calls function correctly");
  278. }
  279. f2();
  280. }
  281. f1();
  282. function f3() {
  283. class OnlyMethod { method() { p('method') } }
  284. class InheritMethod extends OnlyMethod { method2() { p('sub method') } }
  285. class OverrideMethod extends OnlyMethod { method() { p('override method') } }
  286. function f4() {
  287. let inheritMethod = new InheritMethod()
  288. let overrideMethod = new OverrideMethod()
  289. inheritMethod.method();
  290. assert.areEqual('method', m, "derived class inherits base class method");
  291. inheritMethod.method2();
  292. assert.areEqual('sub method', m, "derived class adds new method correctly");
  293. overrideMethod.method();
  294. assert.areEqual('override method', m, "derived class overrides method correctly");
  295. }
  296. f4();
  297. }
  298. f3();
  299. function f5() {
  300. let OnlyMethodExpr = class OnlyMethodExpr { method() { p('class expr method') } }
  301. let OnlyMethodExprNameless = class { method() { p('class expr no name method') } }
  302. function f6() {
  303. let onlyMethodExpr = new OnlyMethodExpr();
  304. let onlyMethodExprNameless = new OnlyMethodExprNameless();
  305. onlyMethodExpr.method();
  306. assert.areEqual('class expr method', m, "method call on class expression works correctly");
  307. onlyMethodExprNameless.method();
  308. assert.areEqual('class expr no name method', m, "method call on class expression with no name works correctly");
  309. }
  310. f6()
  311. }
  312. f5()
  313. function f7() {
  314. class InternalNameUse { static method() { p(InternalNameUse.method.toString()) } }
  315. let InternalNameUseExpr_ = class InternalNameUseExpr { static method() { p(InternalNameUseExpr.method.toString()) } }
  316. function f8() {
  317. InternalNameUse.method();
  318. assert.areEqual('method() { p(InternalNameUse.method.toString()) }', m, "Use of class declaration's name inside method works correctly");
  319. InternalNameUseExpr_.method();
  320. assert.areEqual('method() { p(InternalNameUseExpr.method.toString()) }', m, "Use of class expression's name inside method works correctly");
  321. }
  322. f8()
  323. }
  324. f7()
  325. }
  326. },
  327. {
  328. name: "Invalid uses of super",
  329. body: function () {
  330. class A {
  331. constructor() { p('constructor A'); }
  332. method() { p('method A'); }
  333. }
  334. // Test valid postfix operators in the wrong context
  335. assert.throws(function () { eval("super();") }, SyntaxError, "Invalid use of super", "Invalid use of the 'super' keyword");
  336. assert.throws(function () { eval("super[1];") }, ReferenceError, "Invalid use of super", "Missing or invalid 'super' binding");
  337. assert.throws(function () { eval("super.method();") }, ReferenceError, "Invalid use of super", "Missing or invalid 'super' binding");
  338. // Syntax Error for base class constructor with direct super call
  339. assert.throws(function () { eval("class A { constructor() { super(); } }") }, SyntaxError, "Base class constructor cannot call super");
  340. }
  341. },
  342. {
  343. name: "Basic uses of super",
  344. body: function () {
  345. var ctorCalls = [];
  346. class A {
  347. constructor() { this._initialized = true; ctorCalls.push('A'); }
  348. method() { return 'method A'; }
  349. set initialized(v) { this._initialized = v; }
  350. get initialized() { return this._initialized; }
  351. }
  352. class B extends A {
  353. constructor() {
  354. ctorCalls.push('B pre-super');
  355. super();
  356. ctorCalls.push('B post-super');
  357. }
  358. superMethod() { return super.method() }
  359. superMethodIndex() { return super['method'](); }
  360. getAprop() { return super.initialized; }
  361. setAprop(value) { super.initialized = value; }
  362. getAIndex() { return super['initialized']; }
  363. setAIndex(value) { super['initialized'] = value; }
  364. lambdaIndex() {
  365. var mysuper = x => super[x]();
  366. return mysuper('method');
  367. }
  368. }
  369. let classA = new A();
  370. assert.areEqual(1, ctorCalls.length, "new A calls A's constructor once");
  371. assert.areEqual('A', ctorCalls[0], "new A calls A's constructor");
  372. ctorCalls = [];
  373. let classB = new B();
  374. assert.areEqual(3, ctorCalls.length, "new B calls B and A constructors once each");
  375. assert.areEqual('B pre-super', ctorCalls[0], "new B calls B's constructor first");
  376. assert.areEqual('A', ctorCalls[1], "super within B's constructor calls A's constructor");
  377. assert.areEqual('B post-super', ctorCalls[2], "A's constructor returns to B's constructor after super call");
  378. // Sanity checks
  379. assert.isTrue(classA.method() === 'method A', "classA.method() === 'method A'");
  380. assert.isTrue(classA.initialized === true, "classA.initialized === true");
  381. // Super checks
  382. assert.isTrue(classB.initialized === true, "classB.initialized === true");
  383. assert.isTrue(classB.superMethod() === 'method A', "classB.superMethod() === 'method A'");
  384. assert.isTrue(classB.initialized === true, "classB.initialized === true");
  385. classB.setAprop(123);
  386. assert.isTrue(classB.getAprop() === 123, "classB.getAprop() === 123");
  387. assert.isTrue(classB.getAIndex() === 123, "classB.getAIndex() === 123");
  388. classB.setAIndex(456);
  389. assert.isTrue(classB.getAprop() === 456, "classB.getAprop() === 456");
  390. assert.isTrue(classB.getAIndex() === 456, "classB.getAIndex() === 456");
  391. assert.isTrue(classB.lambdaIndex() === 'method A', "classB.lambdaIndex() === 'method A'");
  392. }
  393. },
  394. {
  395. name: "Super used outside the class declaration function",
  396. body: function () {
  397. class A1
  398. {
  399. method() { return 3; }
  400. };
  401. class A2
  402. {
  403. method() { return 2; }
  404. }
  405. function GetClassB(Asomething)
  406. {
  407. class B extends (Asomething)
  408. {
  409. method() { return 4; }
  410. supermethod() { return super.method(); }
  411. };
  412. return B;
  413. }
  414. let classB1 = GetClassB(A1);
  415. let classB2 = GetClassB(A2);
  416. let b1 = new classB1();
  417. let b2 = new classB2();
  418. assert.isTrue(b1.method() === 4, "b1.method() === 4)");
  419. assert.isTrue(b1.supermethod() === 3, "b1.supermethod() === 3");
  420. assert.isTrue(b2.method() === 4, "b2.method() === 4");
  421. assert.isTrue(b2.supermethod() === 2, "b2.supermethod() === 2");
  422. }
  423. },
  424. {
  425. name: "Super adapts to __proto__ changes",
  426. body: function () {
  427. class A1 {
  428. method() { return "A1"; }
  429. static staticMethod() { return "static A1"; }
  430. }
  431. class A2 {
  432. method() { return "A2"; }
  433. static staticMethod() { return "static A2"; }
  434. }
  435. class A3 {
  436. method() { return "A3"; }
  437. static staticMethod() { return "static A3"; }
  438. }
  439. class B extends A1 {
  440. method() { return super.method(); }
  441. static staticMethod() { return super.staticMethod(); }
  442. }
  443. assert.areEqual(B.__proto__, A1);
  444. assert.areEqual(B.prototype.__proto__, A1.prototype);
  445. let instanceB1 = new B();
  446. let instanceB2 = new B();
  447. assert.areEqual("A1", instanceB1.method());
  448. assert.areEqual("static A1", B.staticMethod());
  449. assert.areEqual(instanceB1.method(), instanceB2.method());
  450. // Change the 'static' part of B
  451. B.__proto__ = A2;
  452. assert.areEqual(B.__proto__, A2);
  453. assert.areEqual(B.prototype.__proto__, A1.prototype);
  454. assert.areEqual("A1", instanceB1.method(), "Instance methods should not be affected by B.__proto__ change");
  455. assert.areEqual("static A2", B.staticMethod(), "Static method should have changed after B.__proto__ change");
  456. assert.areEqual(instanceB1.method(), instanceB2.method(), "All instances should not have been affected by B.__proto__ change");
  457. // Change the 'dynamic' part of B
  458. B.prototype.__proto__ = A3.prototype;
  459. assert.areEqual(B.__proto__, A2);
  460. assert.areEqual(B.prototype.__proto__, A3.prototype);
  461. assert.areEqual("A3", instanceB1.method(), "Instance methods should be affected after B.prototype.__proto__ change");
  462. assert.areEqual("static A2", B.staticMethod(), "Static methods should be unaffected after B.prototype.__proto__ change");
  463. assert.areEqual(instanceB1.method(), instanceB2.method(), "All instances should have been changed by B.prototype.__proto__ change");
  464. }
  465. },
  466. {
  467. name: "super reference in base class constructor",
  468. body: function () {
  469. class A {
  470. constructor() { super.toString(); }
  471. dontDoThis() { super.makeBugs = 1; }
  472. }
  473. class B {
  474. constructor() {
  475. this.string = super.toString();
  476. this.stringCall = super.toString.call(this);
  477. this.stringLambda = (()=>super.toString())() ;
  478. this.stringLambda2 = (()=>(()=>super.toString())())() ;
  479. this.stringEval = eval("super.toString()");
  480. this.stringEval2 = eval("eval(\"super.toString()\")");
  481. this.stringEvalLambda = eval("(()=>super.toString())()");
  482. this.stringEvalLambdaEval = eval("(()=>eval(\"super.toString()\"))()");
  483. this.stringEval2Lambda = eval("eval(\"(()=>super.toString())()\")");
  484. this.stringLambdaEval = (()=>eval("super.toString()"))() ;
  485. this.stringLambda2Eval = (()=>(()=>eval("super.toString()"))())() ;
  486. this.func = super.toString;
  487. this.funcLambda = (()=>super.toString)() ;
  488. this.funcLambda2 = (()=>(()=>super.toString)())() ;
  489. this.funcEval = eval("super.toString");
  490. this.funcEval2 = eval("eval(\"super.toString\")");
  491. this.funcEvalLambda = eval("(()=>super.toString)()");
  492. this.funcEvalLambdaEval = eval("(()=>eval(\"super.toString\"))()");
  493. this.funcEval2Lambda = eval("eval(\"(()=>super.toString)()\")");
  494. this.funcLambdaEval = (()=>eval("super.toString"))() ;
  495. this.funcLambda2Eval = (()=>(()=>eval("super.toString"))())() ;
  496. }
  497. }
  498. assert.areEqual("function", typeof A);
  499. var a = new A();
  500. var b = new B();
  501. a.dontDoThis();
  502. assert.areEqual(1, a.makeBugs);
  503. assert.areEqual("[object Object]", b.string);
  504. assert.areEqual("[object Object]", b.stringCall);
  505. assert.areEqual("[object Object]", b.stringLambda);
  506. assert.areEqual("[object Object]", b.stringLambda2);
  507. assert.areEqual("[object Object]", b.stringEval);
  508. assert.areEqual("[object Object]", b.stringEval2);
  509. assert.areEqual("[object Object]", b.stringEvalLambda);
  510. assert.areEqual("[object Object]", b.stringEvalLambdaEval);
  511. assert.areEqual("[object Object]", b.stringEval2Lambda);
  512. assert.areEqual("[object Object]", b.stringLambdaEval);
  513. assert.areEqual("[object Object]", b.stringLambda2Eval);
  514. assert.areEqual(Object.prototype.toString, b.func);
  515. assert.areEqual(Object.prototype.toString, b.funcLambda);
  516. assert.areEqual(Object.prototype.toString, b.funcLambda2);
  517. assert.areEqual(Object.prototype.toString, b.funcEval);
  518. assert.areEqual(Object.prototype.toString, b.funcEval2);
  519. assert.areEqual(Object.prototype.toString, b.funcEvalLambda);
  520. assert.areEqual(Object.prototype.toString, b.funcEvalLambdaEval);
  521. assert.areEqual(Object.prototype.toString, b.funcEval2Lambda);
  522. assert.areEqual(Object.prototype.toString, b.funcLambdaEval);
  523. assert.areEqual(Object.prototype.toString, b.funcLambda2Eval);
  524. }
  525. },
  526. {
  527. name: "Default constructors",
  528. body: function () {
  529. class a { };
  530. class b extends a { };
  531. assert.areEqual("class a { }", a.prototype.constructor.toString());
  532. assert.areEqual("class b extends a { }", b.prototype.constructor.toString());
  533. var result = [];
  534. var test = [];
  535. class c { constructor() { result = [...arguments]; } };
  536. class d extends c { };
  537. new d();
  538. assert.areEqual(result, [], "Default extends ctor with no args");
  539. test = [1, 2, 3];
  540. new d(...test);
  541. assert.areEqual(result, test, "Default extends ctor with some args");
  542. test = [-5, 4.53, "test", null, undefined, 9348579];
  543. new d(...test);
  544. assert.areEqual(result, test, "Default extends ctor with different arg types");
  545. }
  546. },
  547. {
  548. name: "Evals and lambdas",
  549. body: function () {
  550. class a { method() { return "hello world"; } };
  551. class b extends a {
  552. method1() { return eval("super.method()"); }
  553. method2() { return eval("super['method']()"); }
  554. method3() { return eval("eval('super.method();')"); }
  555. method4() { return eval("x => super.method()")(); }
  556. method5() { return (x => eval("super.method()"))(); }
  557. method6() { return (x => x => x => super.method())()()(); }
  558. method7() { return (x => eval("x => eval('super.method')"))()()(); }
  559. method8() { eval(); return (x => super.method())(); }
  560. method9() { eval(); return (x => function () { return eval("x => super()")(); }())();}
  561. method10(){ var x = () => { eval(""); return super.method(); }; return x(); }
  562. }
  563. let instance = new b();
  564. assert.areEqual("hello world", instance.method1(), "Basic eval use");
  565. assert.areEqual("hello world", instance.method2(), "Basic eval use 2");
  566. assert.areEqual("hello world", instance.method3(), "Nested eval use");
  567. assert.areEqual("hello world", instance.method4(), "Mixed lambda and eval use, no nesting");
  568. assert.areEqual("hello world", instance.method5(), "Mixed lambda and eval use, no nesting 2");
  569. assert.areEqual("hello world", instance.method6(), "Nested lambdas and eval");
  570. assert.areEqual("hello world", instance.method7(), "Nested lambdas and nested evals");
  571. assert.areEqual("hello world", instance.method8(), "Lambda with an eval in the parent");
  572. assert.throws(function() { instance.method9(); }, SyntaxError);
  573. assert.throws(function() { (x => eval('super()'))(); }, SyntaxError);
  574. assert.areEqual("hello world", instance.method10(), "Lambda with an eval in the lambda");
  575. }
  576. },
  577. {
  578. name: "Immutable binding within class body, declarations also have normal let binding in enclosing context",
  579. body: function() {
  580. var c = class k {
  581. reassign() { eval('k = 0; WScript.Echo(k);'); }
  582. };
  583. // Class name is immutable within class body.
  584. var obj1 = new c();
  585. assert.throws(function() { obj1.reassign() }, TypeError);
  586. // Class name is also immutable within body of class declaration statement
  587. class Q extends c {
  588. reassign() { eval('Q = 0;'); }
  589. };
  590. var obj2 = new Q();
  591. assert.throws(function() { obj2.reassign() }, TypeError);
  592. // Class name binding in enclosing context is mutable
  593. Q = 0;
  594. assert.areEqual(Q, 0, "Mutable class declaration binding");
  595. }
  596. },
  597. {
  598. name: "Ensure the super scope slot is emitted at the right time",
  599. body: function () {
  600. // Previously caused an assert in ByteCodeGen.
  601. class a { method () { return "hello" } };
  602. class b extends a { method () { let a; let b; return (x => super.method()); } }
  603. }
  604. },
  605. {
  606. name: "'super' reference in eval() and lambda",
  607. body: function () {
  608. class a {
  609. method() {return "foo"}
  610. }
  611. class b extends a {
  612. method1() { return eval("super.method()") }
  613. method2() { var method= () => super.method(); return method(); }
  614. method3() { return eval("var method= () => super.method(); method();") }
  615. method4() { return eval("var method=function () { return super.method()}; method();") }
  616. method5() { return eval("class a{method(){return 'bar'}}; class b extends a{method(){return super.method()}};(new b()).method()") }
  617. }
  618. let instance = new b();
  619. assert.areEqual("foo",instance.method1(),"'super' in eval()");
  620. assert.areEqual("foo",instance.method2(),"'super' in lambda");
  621. assert.areEqual("foo",instance.method3(),"'super' in lambda in eval");
  622. // TODO: Re-enable the following when our behavior is correct
  623. //assert.throws(function () { instance.method4()}, ReferenceError, "'super' in function body in eval");
  624. assert.areEqual("bar",instance.method5(),"'super' in class method in eval");
  625. }
  626. },
  627. {
  628. name: "Class method can be a generator",
  629. body: function() {
  630. class ClassWithGeneratorMethod {
  631. *iter() {
  632. for (let i of [1,2,3]) {
  633. yield i;
  634. }
  635. }
  636. };
  637. let a = [];
  638. for (let i of new ClassWithGeneratorMethod().iter()) {
  639. a.push(i);
  640. }
  641. assert.areEqual([1,2,3], a, "");
  642. }
  643. },
  644. {
  645. name: "Class method with computed name can be a generator",
  646. body: function() {
  647. class ClassWithGeneratorMethod {
  648. *[Symbol.iterator]() {
  649. for (let i of [1,2,3]) {
  650. yield i;
  651. }
  652. }
  653. };
  654. let a = [];
  655. for (let i of new ClassWithGeneratorMethod()) {
  656. a.push(i);
  657. }
  658. assert.areEqual([1,2,3], a, "");
  659. }
  660. },
  661. {
  662. name: "Class static method descriptor values",
  663. body: function() {
  664. class B {
  665. static method() {
  666. return 'abc';
  667. }
  668. static ['method2']() {
  669. return 'def';
  670. }
  671. static get method3() {
  672. return 'ghi';
  673. }
  674. static get ['method4']() {
  675. return 'jkl';
  676. }
  677. static set method5(x) {
  678. return 'mno';
  679. }
  680. static set ['method6'](x) {
  681. return 'pqr';
  682. }
  683. static *method7() {
  684. yield 'stu';
  685. }
  686. static *['method8']() {
  687. yield 'vwx';
  688. }
  689. }
  690. verifyClassMember(B, 'method', 'abc');
  691. verifyClassMember(B, 'method2', 'def');
  692. verifyClassMember(B, 'method3', 'ghi', true);
  693. verifyClassMember(B, 'method4', 'jkl', true);
  694. verifyClassMember(B, 'method5', 'mno', false, true);
  695. verifyClassMember(B, 'method6', 'pqr', false, true);
  696. verifyClassMember(B, 'method7', 'stu', false, false, true);
  697. verifyClassMember(B, 'method8', 'vwx', false, false, true);
  698. }
  699. },
  700. {
  701. name: "Class method descriptor values",
  702. body: function() {
  703. class B {
  704. method() {
  705. return 'abc';
  706. }
  707. ['method2']() {
  708. return 'def';
  709. }
  710. get method3() {
  711. return 'ghi';
  712. }
  713. get ['method4']() {
  714. return 'jkl';
  715. }
  716. set method5(x) {
  717. return 'mno';
  718. }
  719. set ['method6'](x) {
  720. return 'pqr';
  721. }
  722. *method7() {
  723. yield 'stu';
  724. }
  725. *['method8']() {
  726. yield 'vwx';
  727. }
  728. }
  729. verifyClassMember(B.prototype, 'method', 'abc');
  730. verifyClassMember(B.prototype, 'method2', 'def');
  731. verifyClassMember(B.prototype, 'method3', 'ghi', true);
  732. verifyClassMember(B.prototype, 'method4', 'jkl', true);
  733. verifyClassMember(B.prototype, 'method5', 'mno', false, true);
  734. verifyClassMember(B.prototype, 'method6', 'pqr', false, true);
  735. verifyClassMember(B.prototype, 'method7', 'stu', false, false, true);
  736. verifyClassMember(B.prototype, 'method8', 'vwx', false, false, true);
  737. }
  738. },
  739. {
  740. name: "Class constructor cannot be called without new keyword",
  741. body: function () {
  742. class A {}
  743. assert.throws(function() { A(); }, TypeError, "Base class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
  744. assert.throws(function() { A.call(); }, TypeError, "Base class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
  745. assert.throws(function() { A.apply(); }, TypeError, "Base class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
  746. class B extends A {}
  747. assert.throws(function() { B(); }, TypeError, "Derived class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
  748. assert.throws(function() { B.call(); }, TypeError, "Derived class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
  749. assert.throws(function() { B.apply(); }, TypeError, "Derived class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
  750. class SubArray extends Array { };
  751. assert.throws(function() { SubArray(); }, TypeError, "Class derived from built-in does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
  752. assert.throws(function() { SubArray.call(); }, TypeError, "Class derived from built-in does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
  753. assert.throws(function() { SubArray.apply(); }, TypeError, "Class derived from built-in does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
  754. }
  755. },
  756. {
  757. name: "Class methods cannot be called as constructors",
  758. body: function() {
  759. class B {
  760. method() {
  761. return { foo: 'a' };
  762. }
  763. static method2() {
  764. return { foo: 'b' };
  765. }
  766. }
  767. assert.throws(function() { new B.prototype.method(); }, TypeError, "Base class prototype method cannot be new'd", "Function is not a constructor");
  768. assert.throws(function() { new B.method2(); }, TypeError, "Base class static method cannot be new'd", "Function is not a constructor");
  769. class C extends B {
  770. method3() {
  771. return { foo: 'c' };
  772. }
  773. static method4() {
  774. return { foo: 'd' };
  775. }
  776. }
  777. assert.throws(function() { new C.prototype.method(); }, TypeError, "Base class prototype method cannot be new'd", "Function is not a constructor");
  778. assert.throws(function() { new C.method2(); }, TypeError, "Base class static method cannot be new'd", "Function is not a constructor");
  779. assert.throws(function() { new C.prototype.method3(); }, TypeError, "Derived class prototype method cannot be new'd", "Function is not a constructor");
  780. assert.throws(function() { new C.method4(); }, TypeError, "Derived class static method cannot be new'd", "Function is not a constructor");
  781. class D extends Array {
  782. method5() {
  783. return { foo: 'e' };
  784. }
  785. static method6() {
  786. return { foo: 'f' };
  787. }
  788. }
  789. assert.throws(function() { new D.prototype.method5(); }, TypeError, "Derived class prototype method cannot be new'd", "Function is not a constructor");
  790. assert.throws(function() { new D.method6(); }, TypeError, "Derived class static method cannot be new'd", "Function is not a constructor");
  791. }
  792. },
  793. {
  794. name: "Class static member cannot have computed name 'prototype'",
  795. body: function() {
  796. assert.throws(function() { eval(`class A { static ['prototype']() {} };`); }, TypeError, "Ordinary static member cannot have computed name 'prototype'", "Class static member cannot be named 'prototype'");
  797. assert.throws(function() { eval(`class A { static get ['prototype']() {} };`); }, TypeError, "Static get member cannot have computed name 'prototype'", "Class static member cannot be named 'prototype'");
  798. assert.throws(function() { eval(`class A { static set ['prototype'](x) {} };`); }, TypeError, "Static set member cannot have computed name 'prototype'", "Class static member cannot be named 'prototype'");
  799. assert.throws(function() { eval(`class A { static *['prototype']() {} };`); }, TypeError, "Static generator member cannot have computed name 'prototype'", "Class static member cannot be named 'prototype'");
  800. assert.doesNotThrow(function() { eval(`class A { ['prototype']() {} };`); }, "Class member with computed name 'prototype' is fine");
  801. assert.doesNotThrow(function() { eval(`class A { get ['prototype']() {} };`); }, "Class get member with computed name 'prototype' is fine");
  802. assert.doesNotThrow(function() { eval(`class A { set ['prototype'](x) {} };`); }, "Class set member with computed name 'prototype' is fine");
  803. assert.doesNotThrow(function() { eval(`class A { *['prototype']() {} };`); }, "Class generator member with computed name 'prototype' is fine");
  804. }
  805. },
  806. {
  807. name: "Extends expression of a class declaration or expression is strict mode",
  808. body: function() {
  809. var BadClass = class extends function() { arguments.callee; } {};
  810. assert.throws(function() { Object.getPrototypeOf(BadClass).arguments; }, TypeError, "The extends expression of a class expression should be parsed in strict mode", "'arguments', 'callee' and 'caller' are restricted function properties and cannot be accessed in this context");
  811. assert.throws(function() { new BadClass(); }, TypeError, "New'ing a class with a parent constructor that throws in strict mode, should throw", "'arguments', 'callee' and 'caller' are restricted function properties and cannot be accessed in this context");
  812. assert.throws(function() { eval('class WorseClass extends (function foo() { with ({}); return foo; }()) {};'); }, SyntaxError, "The extends expression of a class decl should be parsed in strict mode", "'with' statements are not allowed in strict mode");
  813. }
  814. },
  815. {
  816. name: "Class identifier is evaluated in strict mode",
  817. body: function() {
  818. assert.throws(function() { eval('class arguments {};'); }, SyntaxError, "A class may not be named arguments because assigning to arguments in strict mode is not allowed", "Invalid usage of 'arguments' in strict mode");
  819. assert.throws(function() { eval('var x = class arguments {};'); }, SyntaxError, "A class may not be named arguments because assigning to arguments in strict mode is not allowed", "Invalid usage of 'arguments' in strict mode");
  820. assert.throws(function() { eval('class eval {};'); }, SyntaxError, "A class may not be named eval because assigning to arguments in strict mode is not allowed", "Invalid usage of 'eval' in strict mode");
  821. assert.throws(function() { eval('var x = class eval {};'); }, SyntaxError, "A class may not be named eval because assigning to arguments in strict mode is not allowed", "Invalid usage of 'eval' in strict mode");
  822. }
  823. },
  824. {
  825. name: "Classes with caller/arguments methods",
  826. body: function() {
  827. class ClassWithArgumentsAndCallerMethods {
  828. static arguments() { return 'abc'; }
  829. static caller() { return 'def'; }
  830. arguments() { return '123'; }
  831. caller() { return '456'; }
  832. }
  833. assert.areEqual('abc', ClassWithArgumentsAndCallerMethods.arguments(), "ClassWithArgumentsAndCallerMethods.arguments() === 'abc'");
  834. assert.areEqual('def', ClassWithArgumentsAndCallerMethods.caller(), "ClassWithArgumentsAndCallerMethods.caller() === 'def'");
  835. assert.areEqual('123', new ClassWithArgumentsAndCallerMethods().arguments(), "new ClassWithArgumentsAndCallerMethods().arguments() === '123'");
  836. assert.areEqual('456', new ClassWithArgumentsAndCallerMethods().caller(), "new ClassWithArgumentsAndCallerMethods().caller() === '456'");
  837. let set_arguments = '';
  838. let set_caller = '';
  839. class ClassWithArgumentsAndCallerAccessors {
  840. static get arguments() { return 'abc'; }
  841. static set arguments(v) { set_arguments = v; }
  842. static get caller() { return 'def'; }
  843. static set caller(v) { set_caller = v; }
  844. get arguments() { return '123'; }
  845. set arguments(v) { set_arguments = v; }
  846. get caller() { return '456'; }
  847. set caller(v) { set_caller = v; }
  848. }
  849. assert.areEqual('abc', ClassWithArgumentsAndCallerAccessors.arguments, "ClassWithArgumentsAndCallerAccessors.arguments === 'abc'");
  850. assert.areEqual('def', ClassWithArgumentsAndCallerAccessors.caller, "ClassWithArgumentsAndCallerAccessors.caller === 'def'");
  851. assert.areEqual('123', new ClassWithArgumentsAndCallerAccessors().arguments, "new ClassWithArgumentsAndCallerAccessors().arguments === '123'");
  852. assert.areEqual('456', new ClassWithArgumentsAndCallerAccessors().caller, "new ClassWithArgumentsAndCallerAccessors().caller === '456'");
  853. ClassWithArgumentsAndCallerAccessors.arguments = 123
  854. assert.areEqual(123, set_arguments, "ClassWithArgumentsAndCallerAccessors.arguments = 123 (calls setter)");
  855. new ClassWithArgumentsAndCallerAccessors().arguments = 456
  856. assert.areEqual(456, set_arguments, "new ClassWithArgumentsAndCallerAccessors().arguments = 456 (calls setter)");
  857. ClassWithArgumentsAndCallerAccessors.caller = 123
  858. assert.areEqual(123, set_caller, "ClassWithArgumentsAndCallerAccessors.caller = 123 (calls setter)");
  859. new ClassWithArgumentsAndCallerAccessors().caller = 456
  860. assert.areEqual(456, set_caller, "new ClassWithArgumentsAndCallerAccessors().caller = 456 (calls setter)");
  861. class ClassWithArgumentsAndCallerGeneratorMethods {
  862. static *arguments() { yield 'abc'; }
  863. static *caller() { yield 'def'; }
  864. *arguments() { yield '123'; }
  865. *caller() { yield '456'; }
  866. }
  867. let s;
  868. for (s of ClassWithArgumentsAndCallerGeneratorMethods.arguments()) {}
  869. assert.areEqual('abc', s, "s of ClassWithArgumentsAndCallerGeneratorMethods.arguments() === 'abc'");
  870. s;
  871. for (s of ClassWithArgumentsAndCallerGeneratorMethods.caller()) {}
  872. assert.areEqual('def', s, "s of ClassWithArgumentsAndCallerGeneratorMethods.caller() === 'def'");
  873. s;
  874. for (s of new ClassWithArgumentsAndCallerGeneratorMethods().arguments()) {}
  875. assert.areEqual('123', s, "s of new ClassWithArgumentsAndCallerGeneratorMethods().arguments() === '123'");
  876. s;
  877. for (s of new ClassWithArgumentsAndCallerGeneratorMethods().caller()) {}
  878. assert.areEqual('456', s, "s of new ClassWithArgumentsAndCallerGeneratorMethods().caller() === '456'");
  879. class ClassWithArgumentsAndCallerComputedNameMethods {
  880. static ['arguments']() { return 'abc'; }
  881. static ['caller']() { return 'def'; }
  882. ['arguments']() { return '123'; }
  883. ['caller']() { return '456'; }
  884. }
  885. assert.areEqual('abc', ClassWithArgumentsAndCallerComputedNameMethods.arguments(), "ClassWithArgumentsAndCallerComputedNameMethods.arguments() === 'abc'");
  886. assert.areEqual('def', ClassWithArgumentsAndCallerComputedNameMethods.caller(), "ClassWithArgumentsAndCallerComputedNameMethods.caller() === 'def'");
  887. assert.areEqual('123', new ClassWithArgumentsAndCallerComputedNameMethods().arguments(), "new ClassWithArgumentsAndCallerComputedNameMethods().arguments() === '123'");
  888. assert.areEqual('456', new ClassWithArgumentsAndCallerComputedNameMethods().caller(), "new ClassWithArgumentsAndCallerComputedNameMethods().caller() === '456'");
  889. class ClassWithArgumentsAndCallerComputedNameAccessors {
  890. static get ['arguments']() { return 'abc'; }
  891. static set ['arguments'](v) { set_arguments = v; }
  892. static get ['caller']() { return 'def'; }
  893. static set ['caller'](v) { set_caller = v; }
  894. get ['arguments']() { return '123'; }
  895. set ['arguments'](v) { set_arguments = v; }
  896. get ['caller']() { return '456'; }
  897. set ['caller'](v) { set_caller = v; }
  898. }
  899. assert.areEqual('abc', ClassWithArgumentsAndCallerAccessors.arguments, "ClassWithArgumentsAndCallerAccessors.arguments === 'abc'");
  900. assert.areEqual('def', ClassWithArgumentsAndCallerAccessors.caller, "ClassWithArgumentsAndCallerAccessors.caller === 'def'");
  901. assert.areEqual('123', new ClassWithArgumentsAndCallerAccessors().arguments, "new ClassWithArgumentsAndCallerAccessors().arguments === '123'");
  902. assert.areEqual('456', new ClassWithArgumentsAndCallerAccessors().caller, "new ClassWithArgumentsAndCallerAccessors().caller === '456'");
  903. ClassWithArgumentsAndCallerAccessors.arguments = 123
  904. assert.areEqual(123, set_arguments, "ClassWithArgumentsAndCallerAccessors.arguments = 123 (calls setter)");
  905. new ClassWithArgumentsAndCallerAccessors().arguments = 456
  906. assert.areEqual(456, set_arguments, "new ClassWithArgumentsAndCallerAccessors().arguments = 456 (calls setter)");
  907. ClassWithArgumentsAndCallerAccessors.caller = 123
  908. assert.areEqual(123, set_caller, "ClassWithArgumentsAndCallerAccessors.caller = 123 (calls setter)");
  909. new ClassWithArgumentsAndCallerAccessors().caller = 456
  910. assert.areEqual(456, set_caller, "new ClassWithArgumentsAndCallerAccessors().caller = 456 (calls setter)");
  911. class ClassWithArgumentsAndCallerComputedNameGeneratorMethods {
  912. static *['arguments']() { yield 'abc'; }
  913. static *['caller']() { yield 'def'; }
  914. *['arguments']() { yield '123'; }
  915. *['caller']() { yield '456'; }
  916. }
  917. s;
  918. for (s of ClassWithArgumentsAndCallerComputedNameGeneratorMethods.arguments()) {}
  919. assert.areEqual('abc', s, "s of ClassWithArgumentsAndCallerComputedNameGeneratorMethods.arguments() === 'abc'");
  920. s;
  921. for (s of ClassWithArgumentsAndCallerComputedNameGeneratorMethods.caller()) {}
  922. assert.areEqual('def', s, "s of ClassWithArgumentsAndCallerComputedNameGeneratorMethods.caller() === 'def'");
  923. s;
  924. for (s of new ClassWithArgumentsAndCallerComputedNameGeneratorMethods().arguments()) {}
  925. assert.areEqual('123', s, "s of new ClassWithArgumentsAndCallerComputedNameGeneratorMethods().arguments() === '123'");
  926. s;
  927. for (s of new ClassWithArgumentsAndCallerComputedNameGeneratorMethods().caller()) {}
  928. assert.areEqual('456', s, "s of new ClassWithArgumentsAndCallerComputedNameGeneratorMethods().caller() === '456'");
  929. }
  930. },
  931. {
  932. name: "toString on constructor should return class declaration or expression",
  933. body: function () {
  934. var B = class { };
  935. var A = class A extends B { constructor (...args) { super(...args); } set x(a) { this._x = a; } set y(a) { this._y = a; } };
  936. class C {
  937. set x(a) { this._x = a; }
  938. set y(a) { this._y = a; }
  939. };
  940. class D { constructor() {} get x() { return 0; } };
  941. var E = D;
  942. assert.areEqual("class A extends B { constructor (...args) { super(...args); } set x(a) { this._x = a; } set y(a) { this._y = a; } }", A.prototype.constructor.toString());
  943. assert.areEqual("class { }", B.prototype.constructor.toString());
  944. assert.areEqual("class C {\r\n set x(a) { this._x = a; }\r\n set y(a) { this._y = a; }\r\n }", C.prototype.constructor.toString());
  945. assert.areEqual("class D { constructor() {} get x() { return 0; } }", D.prototype.constructor.toString());
  946. assert.areEqual("class D { constructor() {} get x() { return 0; } }", E.prototype.constructor.toString());
  947. }
  948. },
  949. {
  950. name: "class getters and setters must take exactly zero and one parameters respectively",
  951. body: function () {
  952. assert.doesNotThrow(function () { eval("class C { get foo() { } }"); }, "Class getter with zero parameters is valid syntax", "asdf");
  953. assert.throws(function () { eval("class C { get foo(x) { } }"); }, SyntaxError, "Class getter with one parameter is invalid syntax", "Getter functions must have no parameters");
  954. assert.throws(function () { eval("class C { get foo(x, y, z) { } }"); }, SyntaxError, "Class getter with more than one parameter is invalid syntax", "Getter functions must have no parameters");
  955. assert.doesNotThrow(function () { eval("class C { set foo(x) { } }"); }, "Class setter with exactly one parameter is valid syntax", "asdf");
  956. assert.throws(function () { eval("class C { set foo() { } }"); }, SyntaxError, "Class setter with zero parameters is invalid syntax", "Setter functions must have exactly one parameter");
  957. assert.throws(function () { eval("class C { set foo(x, y, z) { } }"); }, SyntaxError, "Class setter with more than one parameter is invalid syntax", "Setter functions must have exactly one parameter");
  958. }
  959. },
  960. {
  961. name: "class identifier is const binding inside class body",
  962. body: function () {
  963. assert.throws(function () { class A { constructor() { A = 0; } }; new A(); }, TypeError, "Assignment to class identifier in constructor");
  964. assert.throws(function() { new (class A { constructor() { A = 0; }}); }, TypeError, "Assignment to class identifier in constructor");
  965. assert.throws(function() { class A { m() { A = 0; } }; new A().m(); }, TypeError, "Assignment to class identifier in method");
  966. assert.throws(function() { new (class A { m() { A = 0; } }).m(); }, TypeError, "Assignment to class identifier in method" );
  967. assert.throws(function() { class A { get x() { A = 0; } }; new A().x; }, TypeError, "Assignment to class identifier in getter");
  968. assert.throws(function() { (new (class A { get x() { A = 0; } })).x; }, TypeError, "Assignment to class identifier in getter");
  969. assert.throws(function() { class A { set x(_) { A = 0; } }; new A().x = 15; }, TypeError, "Assignment to class identifier in setter");
  970. assert.throws(function() { (new (class A { set x(_) { A = 0; } })).x = 15; }, TypeError, "Assignment to class identifier in setter");
  971. }
  972. },
  973. {
  974. name: "`class x extends y` where `y` is an expression containing identifier `x` should be a ReferenceError",
  975. body: function() {
  976. var refErrorText = "Use before declaration";
  977. function assert_referr(code) {
  978. assert.throws(function () { eval(code) }, ReferenceError, `\n ${code}`, refErrorText);
  979. }
  980. function assert_referrors(code) {
  981. // Test a given class declaration (where class is named x) by itself and then with `var x =` and `let x =` prepended.
  982. assert_referr(code);
  983. assert_referr(`var x = ${code}`);
  984. assert_referr(`let x = ${code}`);
  985. // Run the same tests, where the class declaration is a wrapped in parens to form a class expression.
  986. // In the Test262 test case, the RHS is a class expression.
  987. // See: test262/test/language/statements/class/name-binding/in-extends-expression-assigned.js
  988. assert_referr(`(${code})`);
  989. assert_referr(`var x = (${code})`);
  990. assert_referr(`let x = (${code})`);
  991. }
  992. function id(x) { return x; };
  993. function fun(x) { return function() { return x }; }
  994. // Using expressions containing the `x` identifier for the extends clause
  995. assert_referrors("class x extends x {}");
  996. assert_referrors("class x extends (x) {}");
  997. assert_referrors("class x extends id(x) {}");
  998. assert_referrors("class x extends fun(x) {}");
  999. // Assigning the result to a different identifier
  1000. assert_referr("var y = class x extends x {}");
  1001. assert_referr("let y = class x extends x {}");
  1002. // Defining `y` after the class to use default initialization (var y) or temporary deadzone (let y)
  1003. assert.throws(function() {
  1004. class x extends y {}; // y == undefined
  1005. var y = function() {};
  1006. }, TypeError, "y is undefined", "Function is not a constructor");
  1007. assert_referr(`
  1008. class x extends y {}; // y is not defined (temporary deadzone)
  1009. let y = function() {};
  1010. `);
  1011. // Using eval where the result of eval is the 'x' identifier or a value that captures the 'x' identifier
  1012. assert_referrors("class x extends eval('x') {}");
  1013. assert_referrors("class x extends eval('(x)') {}");
  1014. assert_referrors("class x extends eval('id(x)') {}");
  1015. assert_referrors("class x extends eval('fun(x)') {}");
  1016. }
  1017. },
  1018. ];
  1019. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
  1020. // Bug 516429 at global scope
  1021. class a {};
  1022. a = null; // No error
  1023. // Bug 257621 at global scope
  1024. assert.doesNotThrow(function () { eval('new (class {})();'); }, "Parenthesized class expressions can be new'd");