| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
- function verifyClassMember(obj, name, expectedReturnValue, isGet, isSet, isGenerator) {
- let p = Object.getOwnPropertyDescriptor(obj, name);
- let strMethodSignature = `obj[${name}](${isGet},${isSet},${isGenerator})`;
- let fn;
- if (isGet) {
- fn = p.get;
- assert.areEqual('function', typeof fn, `${strMethodSignature}: Get method has 'get' property set in descriptor`);
- assert.areEqual(expectedReturnValue, obj[name], `${strMethodSignature}: Invoking class get method returns correct value`);
- assert.areEqual(expectedReturnValue, fn(), `${strMethodSignature}: Calling class get method function directly returns correct value`);
- assert.isFalse('prototype' in fn, `${strMethodSignature}: Class method get does not have 'prototype' property`);
- } else if (isSet) {
- fn = p.set;
- assert.areEqual('function', typeof fn, `${strMethodSignature}: Set method has 'set' property set in descriptor`);
- assert.areEqual(undefined, obj[name], `${strMethodSignature}: Invoking class set method returns undefined`);
- assert.areEqual(expectedReturnValue, fn(), `${strMethodSignature}: Calling class set method function directly returns correct value`);
- assert.isFalse('prototype' in fn, `${strMethodSignature}: Class method set does not have 'prototype' property`);
- } else if (isGenerator) {
- fn = obj[name];
- assert.areEqual('function', typeof fn, `${strMethodSignature}: Class method generator function has correct type`);
- let s;
- for (s of fn()) {}
- assert.areEqual(expectedReturnValue, s, `${strMethodSignature}: Calling class method generator returns correct value`);
- assert.isTrue(p.writable, `${strMethodSignature}: Class method generator functions are writable`);
- assert.isTrue('prototype' in fn, `${strMethodSignature}: Class method generator does have 'prototype' property`);
- } else {
- fn = obj[name]
- assert.areEqual('function', typeof fn, `${strMethodSignature}: Class method has correct type`);
- assert.areEqual(expectedReturnValue, fn(), `${strMethodSignature}: Calling class method returns correct value`);
- // get/set property descriptors do not have writable properties
- assert.isTrue(p.writable, `${strMethodSignature}: Class method functions are writable`);
- assert.isFalse('prototype' in fn, `${strMethodSignature}: Class method does not have 'prototype' property`);
- }
- assert.isFalse(p.enumerable, `${strMethodSignature}: Class methods are not enumerable`);
- assert.isTrue(p.configurable, `${strMethodSignature}: Class methods are configurable`);
- assert.isFalse(fn.hasOwnProperty('arguments'), `${strMethodSignature}: Class methods do not have an 'arguments' property`);
- assert.isFalse(fn.hasOwnProperty('caller'), `${strMethodSignature}: Class methods do not have an 'caller' property`);
- }
- var tests = [
- {
- name: "Class requires an extends expression if the extends keyword is used",
- body: function () {
- assert.throws(function () { eval("class E extends { }") }, SyntaxError);
- }
- },
- {
- name: "Class declarations require a name",
- body: function () {
- assert.throws(function () { eval("class { }") }, SyntaxError);
- }
- },
- {
- name: "Class methods may not have an octal name",
- body: function () {
- assert.throws(function () { eval("class E0 { 0123() {} }") }, SyntaxError, "0123");
- assert.throws(function () { eval("class E1 { 0123.1() {} }") }, SyntaxError, "0123.1");
- }
- },
- {
- name: "Class prototypes must be non-writable",
- body: function () {
- var d = Object.getOwnPropertyDescriptor(class { }, "prototype");
- assert.isFalse(d.writable);
- }
- },
- {
- name: "Class static methods may not be named 'prototype'",
- body: function () {
- assert.throws(function () { eval("class E0 { static prototype() {} }") }, SyntaxError, "static prototype");
- assert.throws(function () { eval("class E1 { static get prototype() {} }") }, SyntaxError, "static get prototype");
- assert.throws(function () { eval("class E2 { static set prototype(x) {} }") }, SyntaxError, "static set prototype");
- }
- },
- {
- name: "Class constructor method can only be a normal method - not getter, setter, or generator",
- body: function () {
- assert.throws(function () { eval("class E { * constructor() {} }") }, SyntaxError, "Class constructor may not be a generator");
- assert.throws(function () { eval("class E0 { get constructor() {} }") }, SyntaxError, "get constructor");
- assert.throws(function () { eval("class E1 { set constructor(x) {} }") }, SyntaxError, "set constructor");
- }
- },
- {
- name: "Class method names can be duplicated; last one lexically always win",
- body: function () {
- assert.throws(function () { eval("class E0 { constructor() {} constructor() {} }") }, SyntaxError, "Duplicated constructor");
- // Valid
- class A { foo() {} foo() {} }
- class B { get foo() {} get foo() {} }
- class C { set foo(x) {} set foo(x) {} }
- class D { get foo() {} set foo(x) {} }
- class E { static foo() {} static foo() {} }
- class F { static get foo() {} static get foo() {} }
- class G { static set foo(x) {} static set foo(x) {} }
- class H { static get foo() {} static set foo(x) {} }
- class I { foo() {} get foo() {} set foo(x) {}}
- class J { static get foo() {} static set foo(x) {} get foo() {} set foo(x) {} }
- class K { static foo() {} static get foo() {} static set foo(x) {}}
- class L { static foo() {} foo() {} }
- class M { static foo() {} get foo() {} set foo(x) {}}
- class N { foo() {} static get foo() {} static set foo(x) {}}
- }
- },
- {
- name: "Class extends expressions must be (null || an object that is a constructor with a prototype that is (null || an object))",
- body: function () {
- class BaseClass {}
- assert.isTrue(Object.getPrototypeOf(BaseClass.prototype) === Object.prototype, "Object.getPrototypeOf(BaseClass.prototype) === Object.prototype")
- assert.isTrue(Object.getPrototypeOf(BaseClass.prototype.constructor) === Function.prototype, "Object.getPrototypeOf(BaseClass.prototype.constructor) === Function.prototype")
- class ExtendsNull extends null { }
- assert.isTrue(Object.getPrototypeOf(ExtendsNull.prototype) === null, "Object.getPrototypeOf(ExtendsNull.prototype) === null")
- assert.isTrue(Object.getPrototypeOf(ExtendsNull.prototype.constructor) === Function.prototype, "Object.getPrototypeOf(ExtendsNull.prototype.constructor) === Function.prototype")
- function NullPrototype () {}
- NullPrototype.prototype = null;
- class ExtendsNullPrototype extends NullPrototype {}
- assert.isTrue(Object.getPrototypeOf(ExtendsNullPrototype.prototype) === null, "Object.getPrototypeOf(ExtendsNullPrototype.prototype) === null")
- assert.isTrue(Object.getPrototypeOf(ExtendsNullPrototype.prototype.constructor) === NullPrototype, "Object.getPrototypeOf(ExtendsNullPrototype.prototype.constructor) === NullPrototype")
- class ExtendsObject extends Object {}
- assert.isTrue(Object.getPrototypeOf(ExtendsObject.prototype) === Object.prototype, "Object.getPrototypeOf(ExtendsObject.prototype) === Object.prototype")
- assert.isTrue(Object.getPrototypeOf(ExtendsObject.prototype.constructor) === Object, "Object.getPrototypeOf(ExtendsObject.prototype.constructor) === Object")
- function Func () {}
- class ExtendsFunc extends Func {}
- assert.isTrue(Object.getPrototypeOf(ExtendsFunc.prototype) === Func.prototype, "Object.getPrototypeOf(ExtendsFunc.prototype) === Func.prototype")
- assert.isTrue(Object.getPrototypeOf(ExtendsFunc.prototype.constructor) === Func, "Object.getPrototypeOf(ExtendsFunc.prototype.constructor) === Func")
- assert.throws(function () { class A extends 0 { } }, TypeError, "Integer extends");
- assert.throws(function () { class A extends "test" { } }, TypeError, "String extends");
- assert.throws(function () { class A extends {} { } }, TypeError, "Object literal extends");
- assert.throws(function () { class A extends undefined { } }, TypeError, "Undefined extends");
- assert.throws(
- function () {
- function Foo() {}
- Foo.prototype = 0;
- class A extends Foo { }
- }, TypeError, "Integer prototype");
- assert.throws(
- function () {
- function Foo() {}
- Foo.prototype = "test";
- class A extends Foo { }
- }, TypeError, "String prototype");
- assert.throws(
- function () {
- function Foo() {}
- Foo.prototype = undefined;
- class A extends Foo { }
- }, TypeError, "Undefined prototype");
- assert.doesNotThrow(function () { eval("class Foo extends new Proxy(class Bar {},{}){}"); });
- }
- },
- {
- name: "Class basic sanity tests",
- body: function () {
- var m;
- function p(x) {
- m = x;
- }
- class Empty { }
- class EmptySemi { ; }
- class OnlyCtor { constructor() { p('ctor') } }
- class OnlyMethod { method() { p('method') } }
- class OnlyStaticMethod { static method() { p('smethod') } }
- class OnlyGetter { get getter() { p('getter') } }
- class OnlyStaticGetter { static get getter() { p('sgetter') } }
- class OnlySetter { set setter(x) { p('setter ' + x) } }
- class OnlyStaticSetter { static set setter(x) { p('ssetter ' + x) } }
- let empty = new Empty();
- let emptySemi = new EmptySemi();
- let onlyCtor = new OnlyCtor();
- assert.areEqual('ctor', m, "constructing OnlyCtor class calls the constructor method");
- let onlyMethod = new OnlyMethod();
- let onlyStaticMethod = new OnlyStaticMethod();
- let onlyGetter = new OnlyGetter();
- let onlyStaticGetter = new OnlyStaticGetter();
- let onlySetter = new OnlySetter();
- let onlyStaticSetter = new OnlyStaticSetter();
- onlyMethod.method();
- assert.areEqual('method', m, "method calls function correctly");
- OnlyStaticMethod.method();
- assert.areEqual('smethod', m, "static method calls function correctly");
- onlyGetter.getter;
- assert.areEqual('getter', m, "getter calls function correctly");
- OnlyStaticGetter.getter;
- assert.areEqual('sgetter', m, "static getter calls function correctly");
- onlySetter.setter = null;
- assert.areEqual('setter null', m, "setter calls function correctly");
- OnlyStaticSetter.setter = null;
- assert.areEqual('ssetter null', m, "static setter calls function correctly");
- class InheritMethod extends OnlyMethod { method2() { p('sub method') } }
- class OverrideMethod extends OnlyMethod { method() { p('override method') } }
- let inheritMethod = new InheritMethod();
- let overrideMethod = new OverrideMethod();
- inheritMethod.method();
- assert.areEqual('method', m, "derived class inherits base class method");
- inheritMethod.method2();
- assert.areEqual('sub method', m, "derived class adds new method correctly");
- overrideMethod.method();
- assert.areEqual('override method', m, "derived class overrides method correctly");
- let OnlyMethodExpr = class OnlyMethodExpr { method() { p('class expr method') } }
- let OnlyMethodExprNameless = class { method() { p('class expr no name method') } }
- let onlyMethodExpr = new OnlyMethodExpr();
- let onlyMethodExprNameless = new OnlyMethodExprNameless();
- onlyMethodExpr.method();
- assert.areEqual('class expr method', m, "method call on class expression works correctly");
- onlyMethodExprNameless.method();
- assert.areEqual('class expr no name method', m, "method call on class expression with no name works correctly");
- class InternalNameUse { static method() { p(InternalNameUse.method.toString()) } }
- let InternalNameUseExpr_ = class InternalNameUseExpr { static method() { p(InternalNameUseExpr.method.toString()) } }
- InternalNameUse.method();
- assert.areEqual('method() { p(InternalNameUse.method.toString()) }', m, "Use of class declaration's name inside method works correctly");
- InternalNameUseExpr_.method();
- assert.areEqual('method() { p(InternalNameUseExpr.method.toString()) }', m, "Use of class expression's name inside method works correctly");
- }
- },
- {
- name: "Class basic sanity tests in closures",
- body: function () {
- var m;
- function p(x) {
- m = x;
- }
- function f1() {
- class Empty { }
- class EmptySemi { ; }
- class OnlyCtor { constructor() { p('ctor') } }
- class OnlyMethod { method() { p('method') } }
- class OnlyStaticMethod { static method() { p('smethod') } }
- class OnlyGetter { get getter() { p('getter') } }
- class OnlyStaticGetter { static get getter() { p('sgetter') } }
- class OnlySetter { set setter(x) { p('setter ' + x) } }
- class OnlyStaticSetter { static set setter(x) { p('ssetter ' + x) } }
- class OnlyComputedMethod { ["cmethod"]() { p('cmethod') } }
- class OnlyStaticComputedMethod { static ["cmethod"]() { p('scmethod') } }
- class OnlyComputedGetter { get ["cgetter"]() { p('cgetter') } }
- class OnlyStaticComputedGetter { static get ["cgetter"]() { p('scgetter') } }
- class OnlyComputedSetter { set ["csetter"](x) { p('csetter ' + x) } }
- class OnlyStaticComputedSetter { static set ["csetter"](x) { p('scsetter ' + x) } }
- function f2() {
- let empty = new Empty();
- let emptySemi = new EmptySemi();
- let onlyCtor = new OnlyCtor();
- assert.areEqual('ctor', m, "constructing OnlyCtor class calls the constructor method");
- let onlyMethod = new OnlyMethod();
- let onlyStaticMethod = new OnlyStaticMethod();
- let onlyGetter = new OnlyGetter();
- let onlyStaticGetter = new OnlyStaticGetter();
- let onlySetter = new OnlySetter();
- let onlyStaticSetter = new OnlyStaticSetter();
- let onlyComputedMethod = new OnlyComputedMethod();
- let onlyComputedGetter = new OnlyComputedGetter();
- let onlyComputedSetter = new OnlyComputedSetter();
- onlyMethod.method();
- assert.areEqual('method', m, "method calls function correctly");
- OnlyStaticMethod.method();
- assert.areEqual('smethod', m, "static method calls function correctly");
- onlyGetter.getter;
- assert.areEqual('getter', m, "getter calls function correctly");
- OnlyStaticGetter.getter;
- assert.areEqual('sgetter', m, "static getter calls function correctly");
- onlySetter.setter = null;
- assert.areEqual('setter null', m, "setter calls function correctly");
- OnlyStaticSetter.setter = null;
- assert.areEqual('ssetter null', m, "static setter calls function correctly");
- onlyComputedMethod.cmethod()
- assert.areEqual('cmethod', m, "computed name method calls function correctly");
- OnlyStaticComputedMethod.cmethod()
- assert.areEqual('scmethod', m, "static computed name method calls function correctly");
- onlyComputedGetter.cgetter;
- assert.areEqual('cgetter', m, "computed name getter calls function correctly");
- OnlyStaticComputedGetter.cgetter;
- assert.areEqual('scgetter', m, "static computed name getter calls function correctly");
- onlyComputedSetter.csetter = null;
- assert.areEqual('csetter null', m, "computed name setter calls function correctly");
- OnlyStaticComputedSetter.csetter = null;
- assert.areEqual('scsetter null', m, "static computed name setter calls function correctly");
- }
- f2();
- }
- f1();
- function f3() {
- class OnlyMethod { method() { p('method') } }
- class InheritMethod extends OnlyMethod { method2() { p('sub method') } }
- class OverrideMethod extends OnlyMethod { method() { p('override method') } }
- function f4() {
- let inheritMethod = new InheritMethod()
- let overrideMethod = new OverrideMethod()
- inheritMethod.method();
- assert.areEqual('method', m, "derived class inherits base class method");
- inheritMethod.method2();
- assert.areEqual('sub method', m, "derived class adds new method correctly");
- overrideMethod.method();
- assert.areEqual('override method', m, "derived class overrides method correctly");
- }
- f4();
- }
- f3();
- function f5() {
- let OnlyMethodExpr = class OnlyMethodExpr { method() { p('class expr method') } }
- let OnlyMethodExprNameless = class { method() { p('class expr no name method') } }
- function f6() {
- let onlyMethodExpr = new OnlyMethodExpr();
- let onlyMethodExprNameless = new OnlyMethodExprNameless();
- onlyMethodExpr.method();
- assert.areEqual('class expr method', m, "method call on class expression works correctly");
- onlyMethodExprNameless.method();
- assert.areEqual('class expr no name method', m, "method call on class expression with no name works correctly");
- }
- f6()
- }
- f5()
- function f7() {
- class InternalNameUse { static method() { p(InternalNameUse.method.toString()) } }
- let InternalNameUseExpr_ = class InternalNameUseExpr { static method() { p(InternalNameUseExpr.method.toString()) } }
- function f8() {
- InternalNameUse.method();
- assert.areEqual('method() { p(InternalNameUse.method.toString()) }', m, "Use of class declaration's name inside method works correctly");
- InternalNameUseExpr_.method();
- assert.areEqual('method() { p(InternalNameUseExpr.method.toString()) }', m, "Use of class expression's name inside method works correctly");
- }
- f8()
- }
- f7()
- }
- },
- {
- name: "Invalid uses of super",
- body: function () {
- class A {
- constructor() { p('constructor A'); }
- method() { p('method A'); }
- }
- // Test valid postfix operators in the wrong context
- assert.throws(function () { eval("super();") }, SyntaxError, "Invalid use of super", "Invalid use of the 'super' keyword");
- assert.throws(function () { eval("super[1];") }, ReferenceError, "Invalid use of super", "Missing or invalid 'super' binding");
- assert.throws(function () { eval("super.method();") }, ReferenceError, "Invalid use of super", "Missing or invalid 'super' binding");
- // Syntax Error for base class constructor with direct super call
- assert.throws(function () { eval("class A { constructor() { super(); } }") }, SyntaxError, "Base class constructor cannot call super");
- }
- },
- {
- name: "Basic uses of super",
- body: function () {
- var ctorCalls = [];
- class A {
- constructor() { this._initialized = true; ctorCalls.push('A'); }
- method() { return 'method A'; }
- set initialized(v) { this._initialized = v; }
- get initialized() { return this._initialized; }
- }
- class B extends A {
- constructor() {
- ctorCalls.push('B pre-super');
- super();
- ctorCalls.push('B post-super');
- }
- superMethod() { return super.method() }
- superMethodIndex() { return super['method'](); }
- getAprop() { return super.initialized; }
- setAprop(value) { super.initialized = value; }
- getAIndex() { return super['initialized']; }
- setAIndex(value) { super['initialized'] = value; }
- lambdaIndex() {
- var mysuper = x => super[x]();
- return mysuper('method');
- }
- }
- let classA = new A();
- assert.areEqual(1, ctorCalls.length, "new A calls A's constructor once");
- assert.areEqual('A', ctorCalls[0], "new A calls A's constructor");
- ctorCalls = [];
- let classB = new B();
- assert.areEqual(3, ctorCalls.length, "new B calls B and A constructors once each");
- assert.areEqual('B pre-super', ctorCalls[0], "new B calls B's constructor first");
- assert.areEqual('A', ctorCalls[1], "super within B's constructor calls A's constructor");
- assert.areEqual('B post-super', ctorCalls[2], "A's constructor returns to B's constructor after super call");
- // Sanity checks
- assert.isTrue(classA.method() === 'method A', "classA.method() === 'method A'");
- assert.isTrue(classA.initialized === true, "classA.initialized === true");
- // Super checks
- assert.isTrue(classB.initialized === true, "classB.initialized === true");
- assert.isTrue(classB.superMethod() === 'method A', "classB.superMethod() === 'method A'");
- assert.isTrue(classB.initialized === true, "classB.initialized === true");
- classB.setAprop(123);
- assert.isTrue(classB.getAprop() === 123, "classB.getAprop() === 123");
- assert.isTrue(classB.getAIndex() === 123, "classB.getAIndex() === 123");
- classB.setAIndex(456);
- assert.isTrue(classB.getAprop() === 456, "classB.getAprop() === 456");
- assert.isTrue(classB.getAIndex() === 456, "classB.getAIndex() === 456");
- assert.isTrue(classB.lambdaIndex() === 'method A', "classB.lambdaIndex() === 'method A'");
- }
- },
- {
- name: "Super used outside the class declaration function",
- body: function () {
- class A1
- {
- method() { return 3; }
- };
- class A2
- {
- method() { return 2; }
- }
- function GetClassB(Asomething)
- {
- class B extends (Asomething)
- {
- method() { return 4; }
- supermethod() { return super.method(); }
- };
- return B;
- }
- let classB1 = GetClassB(A1);
- let classB2 = GetClassB(A2);
- let b1 = new classB1();
- let b2 = new classB2();
- assert.isTrue(b1.method() === 4, "b1.method() === 4)");
- assert.isTrue(b1.supermethod() === 3, "b1.supermethod() === 3");
- assert.isTrue(b2.method() === 4, "b2.method() === 4");
- assert.isTrue(b2.supermethod() === 2, "b2.supermethod() === 2");
- }
- },
- {
- name: "Super adapts to __proto__ changes",
- body: function () {
- class A1 {
- method() { return "A1"; }
- static staticMethod() { return "static A1"; }
- }
- class A2 {
- method() { return "A2"; }
- static staticMethod() { return "static A2"; }
- }
- class A3 {
- method() { return "A3"; }
- static staticMethod() { return "static A3"; }
- }
- class B extends A1 {
- method() { return super.method(); }
- static staticMethod() { return super.staticMethod(); }
- }
- assert.areEqual(B.__proto__, A1);
- assert.areEqual(B.prototype.__proto__, A1.prototype);
- let instanceB1 = new B();
- let instanceB2 = new B();
- assert.areEqual("A1", instanceB1.method());
- assert.areEqual("static A1", B.staticMethod());
- assert.areEqual(instanceB1.method(), instanceB2.method());
- // Change the 'static' part of B
- B.__proto__ = A2;
- assert.areEqual(B.__proto__, A2);
- assert.areEqual(B.prototype.__proto__, A1.prototype);
- assert.areEqual("A1", instanceB1.method(), "Instance methods should not be affected by B.__proto__ change");
- assert.areEqual("static A2", B.staticMethod(), "Static method should have changed after B.__proto__ change");
- assert.areEqual(instanceB1.method(), instanceB2.method(), "All instances should not have been affected by B.__proto__ change");
- // Change the 'dynamic' part of B
- B.prototype.__proto__ = A3.prototype;
- assert.areEqual(B.__proto__, A2);
- assert.areEqual(B.prototype.__proto__, A3.prototype);
- assert.areEqual("A3", instanceB1.method(), "Instance methods should be affected after B.prototype.__proto__ change");
- assert.areEqual("static A2", B.staticMethod(), "Static methods should be unaffected after B.prototype.__proto__ change");
- assert.areEqual(instanceB1.method(), instanceB2.method(), "All instances should have been changed by B.prototype.__proto__ change");
- }
- },
- {
- name: "super reference in base class constructor",
- body: function () {
- class A {
- constructor() { super.toString(); }
- dontDoThis() { super.makeBugs = 1; }
- }
- class B {
- constructor() {
- this.string = super.toString();
- this.stringCall = super.toString.call(this);
- this.stringLambda = (()=>super.toString())() ;
- this.stringLambda2 = (()=>(()=>super.toString())())() ;
- this.stringEval = eval("super.toString()");
- this.stringEval2 = eval("eval(\"super.toString()\")");
- this.stringEvalLambda = eval("(()=>super.toString())()");
- this.stringEvalLambdaEval = eval("(()=>eval(\"super.toString()\"))()");
- this.stringEval2Lambda = eval("eval(\"(()=>super.toString())()\")");
- this.stringLambdaEval = (()=>eval("super.toString()"))() ;
- this.stringLambda2Eval = (()=>(()=>eval("super.toString()"))())() ;
- this.func = super.toString;
- this.funcLambda = (()=>super.toString)() ;
- this.funcLambda2 = (()=>(()=>super.toString)())() ;
- this.funcEval = eval("super.toString");
- this.funcEval2 = eval("eval(\"super.toString\")");
- this.funcEvalLambda = eval("(()=>super.toString)()");
- this.funcEvalLambdaEval = eval("(()=>eval(\"super.toString\"))()");
- this.funcEval2Lambda = eval("eval(\"(()=>super.toString)()\")");
- this.funcLambdaEval = (()=>eval("super.toString"))() ;
- this.funcLambda2Eval = (()=>(()=>eval("super.toString"))())() ;
- }
- }
- assert.areEqual("function", typeof A);
- var a = new A();
- var b = new B();
- a.dontDoThis();
- assert.areEqual(1, a.makeBugs);
- assert.areEqual("[object Object]", b.string);
- assert.areEqual("[object Object]", b.stringCall);
- assert.areEqual("[object Object]", b.stringLambda);
- assert.areEqual("[object Object]", b.stringLambda2);
- assert.areEqual("[object Object]", b.stringEval);
- assert.areEqual("[object Object]", b.stringEval2);
- assert.areEqual("[object Object]", b.stringEvalLambda);
- assert.areEqual("[object Object]", b.stringEvalLambdaEval);
- assert.areEqual("[object Object]", b.stringEval2Lambda);
- assert.areEqual("[object Object]", b.stringLambdaEval);
- assert.areEqual("[object Object]", b.stringLambda2Eval);
- assert.areEqual(Object.prototype.toString, b.func);
- assert.areEqual(Object.prototype.toString, b.funcLambda);
- assert.areEqual(Object.prototype.toString, b.funcLambda2);
- assert.areEqual(Object.prototype.toString, b.funcEval);
- assert.areEqual(Object.prototype.toString, b.funcEval2);
- assert.areEqual(Object.prototype.toString, b.funcEvalLambda);
- assert.areEqual(Object.prototype.toString, b.funcEvalLambdaEval);
- assert.areEqual(Object.prototype.toString, b.funcEval2Lambda);
- assert.areEqual(Object.prototype.toString, b.funcLambdaEval);
- assert.areEqual(Object.prototype.toString, b.funcLambda2Eval);
- }
- },
- {
- name: "Default constructors",
- body: function () {
- class a { };
- class b extends a { };
- assert.areEqual("class a { }", a.prototype.constructor.toString());
- assert.areEqual("class b extends a { }", b.prototype.constructor.toString());
- var result = [];
- var test = [];
- class c { constructor() { result = [...arguments]; } };
- class d extends c { };
- new d();
- assert.areEqual(result, [], "Default extends ctor with no args");
- test = [1, 2, 3];
- new d(...test);
- assert.areEqual(result, test, "Default extends ctor with some args");
- test = [-5, 4.53, "test", null, undefined, 9348579];
- new d(...test);
- assert.areEqual(result, test, "Default extends ctor with different arg types");
- }
- },
- {
- name: "Evals and lambdas",
- body: function () {
- class a { method() { return "hello world"; } };
- class b extends a {
- method1() { return eval("super.method()"); }
- method2() { return eval("super['method']()"); }
- method3() { return eval("eval('super.method();')"); }
- method4() { return eval("x => super.method()")(); }
- method5() { return (x => eval("super.method()"))(); }
- method6() { return (x => x => x => super.method())()()(); }
- method7() { return (x => eval("x => eval('super.method')"))()()(); }
- method8() { eval(); return (x => super.method())(); }
- method9() { eval(); return (x => function () { return eval("x => super()")(); }())();}
- method10(){ var x = () => { eval(""); return super.method(); }; return x(); }
- }
- let instance = new b();
- assert.areEqual("hello world", instance.method1(), "Basic eval use");
- assert.areEqual("hello world", instance.method2(), "Basic eval use 2");
- assert.areEqual("hello world", instance.method3(), "Nested eval use");
- assert.areEqual("hello world", instance.method4(), "Mixed lambda and eval use, no nesting");
- assert.areEqual("hello world", instance.method5(), "Mixed lambda and eval use, no nesting 2");
- assert.areEqual("hello world", instance.method6(), "Nested lambdas and eval");
- assert.areEqual("hello world", instance.method7(), "Nested lambdas and nested evals");
- assert.areEqual("hello world", instance.method8(), "Lambda with an eval in the parent");
- assert.throws(function() { instance.method9(); }, SyntaxError);
- assert.throws(function() { (x => eval('super()'))(); }, SyntaxError);
- assert.areEqual("hello world", instance.method10(), "Lambda with an eval in the lambda");
- }
- },
- {
- name: "Immutable binding within class body, declarations also have normal let binding in enclosing context",
- body: function() {
- var c = class k {
- reassign() { eval('k = 0; WScript.Echo(k);'); }
- };
- // Class name is immutable within class body.
- var obj1 = new c();
- assert.throws(function() { obj1.reassign() }, TypeError);
- // Class name is also immutable within body of class declaration statement
- class Q extends c {
- reassign() { eval('Q = 0;'); }
- };
- var obj2 = new Q();
- assert.throws(function() { obj2.reassign() }, TypeError);
- // Class name binding in enclosing context is mutable
- Q = 0;
- assert.areEqual(Q, 0, "Mutable class declaration binding");
- }
- },
- {
- name: "Ensure the super scope slot is emitted at the right time",
- body: function () {
- // Previously caused an assert in ByteCodeGen.
- class a { method () { return "hello" } };
- class b extends a { method () { let a; let b; return (x => super.method()); } }
- }
- },
- {
- name: "'super' reference in eval() and lambda",
- body: function () {
- class a {
- method() {return "foo"}
- }
- class b extends a {
- method1() { return eval("super.method()") }
- method2() { var method= () => super.method(); return method(); }
- method3() { return eval("var method= () => super.method(); method();") }
- method4() { return eval("var method=function () { return super.method()}; method();") }
- method5() { return eval("class a{method(){return 'bar'}}; class b extends a{method(){return super.method()}};(new b()).method()") }
- }
- let instance = new b();
- assert.areEqual("foo",instance.method1(),"'super' in eval()");
- assert.areEqual("foo",instance.method2(),"'super' in lambda");
- assert.areEqual("foo",instance.method3(),"'super' in lambda in eval");
- // TODO: Re-enable the following when our behavior is correct
- //assert.throws(function () { instance.method4()}, ReferenceError, "'super' in function body in eval");
- assert.areEqual("bar",instance.method5(),"'super' in class method in eval");
- }
- },
- {
- name: "Class method can be a generator",
- body: function() {
- class ClassWithGeneratorMethod {
- *iter() {
- for (let i of [1,2,3]) {
- yield i;
- }
- }
- };
- let a = [];
- for (let i of new ClassWithGeneratorMethod().iter()) {
- a.push(i);
- }
- assert.areEqual([1,2,3], a, "");
- }
- },
- {
- name: "Class method with computed name can be a generator",
- body: function() {
- class ClassWithGeneratorMethod {
- *[Symbol.iterator]() {
- for (let i of [1,2,3]) {
- yield i;
- }
- }
- };
- let a = [];
- for (let i of new ClassWithGeneratorMethod()) {
- a.push(i);
- }
- assert.areEqual([1,2,3], a, "");
- }
- },
- {
- name: "Class static method descriptor values",
- body: function() {
- class B {
- static method() {
- return 'abc';
- }
- static ['method2']() {
- return 'def';
- }
- static get method3() {
- return 'ghi';
- }
- static get ['method4']() {
- return 'jkl';
- }
- static set method5(x) {
- return 'mno';
- }
- static set ['method6'](x) {
- return 'pqr';
- }
- static *method7() {
- yield 'stu';
- }
- static *['method8']() {
- yield 'vwx';
- }
- }
- verifyClassMember(B, 'method', 'abc');
- verifyClassMember(B, 'method2', 'def');
- verifyClassMember(B, 'method3', 'ghi', true);
- verifyClassMember(B, 'method4', 'jkl', true);
- verifyClassMember(B, 'method5', 'mno', false, true);
- verifyClassMember(B, 'method6', 'pqr', false, true);
- verifyClassMember(B, 'method7', 'stu', false, false, true);
- verifyClassMember(B, 'method8', 'vwx', false, false, true);
- }
- },
- {
- name: "Class method descriptor values",
- body: function() {
- class B {
- method() {
- return 'abc';
- }
- ['method2']() {
- return 'def';
- }
- get method3() {
- return 'ghi';
- }
- get ['method4']() {
- return 'jkl';
- }
- set method5(x) {
- return 'mno';
- }
- set ['method6'](x) {
- return 'pqr';
- }
- *method7() {
- yield 'stu';
- }
- *['method8']() {
- yield 'vwx';
- }
- }
- verifyClassMember(B.prototype, 'method', 'abc');
- verifyClassMember(B.prototype, 'method2', 'def');
- verifyClassMember(B.prototype, 'method3', 'ghi', true);
- verifyClassMember(B.prototype, 'method4', 'jkl', true);
- verifyClassMember(B.prototype, 'method5', 'mno', false, true);
- verifyClassMember(B.prototype, 'method6', 'pqr', false, true);
- verifyClassMember(B.prototype, 'method7', 'stu', false, false, true);
- verifyClassMember(B.prototype, 'method8', 'vwx', false, false, true);
- }
- },
- {
- name: "Class constructor cannot be called without new keyword",
- body: function () {
- class A {}
- assert.throws(function() { A(); }, TypeError, "Base class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
- assert.throws(function() { A.call(); }, TypeError, "Base class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
- assert.throws(function() { A.apply(); }, TypeError, "Base class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
- class B extends A {}
- assert.throws(function() { B(); }, TypeError, "Derived class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
- assert.throws(function() { B.call(); }, TypeError, "Derived class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
- assert.throws(function() { B.apply(); }, TypeError, "Derived class constructor does not have a [[call]] slot", "Class constructor cannot be called without the new keyword");
- class SubArray extends Array { };
- 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");
- 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");
- 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");
- }
- },
- {
- name: "Class methods cannot be called as constructors",
- body: function() {
- class B {
- method() {
- return { foo: 'a' };
- }
- static method2() {
- return { foo: 'b' };
- }
- }
- assert.throws(function() { new B.prototype.method(); }, TypeError, "Base class prototype method cannot be new'd", "Function is not a constructor");
- assert.throws(function() { new B.method2(); }, TypeError, "Base class static method cannot be new'd", "Function is not a constructor");
- class C extends B {
- method3() {
- return { foo: 'c' };
- }
- static method4() {
- return { foo: 'd' };
- }
- }
- assert.throws(function() { new C.prototype.method(); }, TypeError, "Base class prototype method cannot be new'd", "Function is not a constructor");
- assert.throws(function() { new C.method2(); }, TypeError, "Base class static method cannot be new'd", "Function is not a constructor");
- assert.throws(function() { new C.prototype.method3(); }, TypeError, "Derived class prototype method cannot be new'd", "Function is not a constructor");
- assert.throws(function() { new C.method4(); }, TypeError, "Derived class static method cannot be new'd", "Function is not a constructor");
- class D extends Array {
- method5() {
- return { foo: 'e' };
- }
- static method6() {
- return { foo: 'f' };
- }
- }
- assert.throws(function() { new D.prototype.method5(); }, TypeError, "Derived class prototype method cannot be new'd", "Function is not a constructor");
- assert.throws(function() { new D.method6(); }, TypeError, "Derived class static method cannot be new'd", "Function is not a constructor");
- }
- },
- {
- name: "Class static member cannot have computed name 'prototype'",
- body: function() {
- assert.throws(function() { eval(`class A { static ['prototype']() {} };`); }, TypeError, "Ordinary static member cannot have computed name 'prototype'", "Class static member cannot be named 'prototype'");
- 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'");
- 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'");
- assert.throws(function() { eval(`class A { static *['prototype']() {} };`); }, TypeError, "Static generator member cannot have computed name 'prototype'", "Class static member cannot be named 'prototype'");
- assert.doesNotThrow(function() { eval(`class A { ['prototype']() {} };`); }, "Class member with computed name 'prototype' is fine");
- assert.doesNotThrow(function() { eval(`class A { get ['prototype']() {} };`); }, "Class get member with computed name 'prototype' is fine");
- assert.doesNotThrow(function() { eval(`class A { set ['prototype'](x) {} };`); }, "Class set member with computed name 'prototype' is fine");
- assert.doesNotThrow(function() { eval(`class A { *['prototype']() {} };`); }, "Class generator member with computed name 'prototype' is fine");
- }
- },
- {
- name: "Extends expression of a class declaration or expression is strict mode",
- body: function() {
- var BadClass = class extends function() { arguments.caller; } {};
- 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");
- 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");
- 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");
- }
- },
- {
- name: "Class identifier is evaluated in strict mode",
- body: function() {
- 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");
- 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");
- 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");
- 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");
- }
- },
- {
- name: "Classes with caller/arguments methods",
- body: function() {
- class ClassWithArgumentsAndCallerMethods {
- static arguments() { return 'abc'; }
- static caller() { return 'def'; }
- arguments() { return '123'; }
- caller() { return '456'; }
- }
- assert.areEqual('abc', ClassWithArgumentsAndCallerMethods.arguments(), "ClassWithArgumentsAndCallerMethods.arguments() === 'abc'");
- assert.areEqual('def', ClassWithArgumentsAndCallerMethods.caller(), "ClassWithArgumentsAndCallerMethods.caller() === 'def'");
- assert.areEqual('123', new ClassWithArgumentsAndCallerMethods().arguments(), "new ClassWithArgumentsAndCallerMethods().arguments() === '123'");
- assert.areEqual('456', new ClassWithArgumentsAndCallerMethods().caller(), "new ClassWithArgumentsAndCallerMethods().caller() === '456'");
- let set_arguments = '';
- let set_caller = '';
- class ClassWithArgumentsAndCallerAccessors {
- static get arguments() { return 'abc'; }
- static set arguments(v) { set_arguments = v; }
- static get caller() { return 'def'; }
- static set caller(v) { set_caller = v; }
- get arguments() { return '123'; }
- set arguments(v) { set_arguments = v; }
- get caller() { return '456'; }
- set caller(v) { set_caller = v; }
- }
- assert.areEqual('abc', ClassWithArgumentsAndCallerAccessors.arguments, "ClassWithArgumentsAndCallerAccessors.arguments === 'abc'");
- assert.areEqual('def', ClassWithArgumentsAndCallerAccessors.caller, "ClassWithArgumentsAndCallerAccessors.caller === 'def'");
- assert.areEqual('123', new ClassWithArgumentsAndCallerAccessors().arguments, "new ClassWithArgumentsAndCallerAccessors().arguments === '123'");
- assert.areEqual('456', new ClassWithArgumentsAndCallerAccessors().caller, "new ClassWithArgumentsAndCallerAccessors().caller === '456'");
- ClassWithArgumentsAndCallerAccessors.arguments = 123
- assert.areEqual(123, set_arguments, "ClassWithArgumentsAndCallerAccessors.arguments = 123 (calls setter)");
- new ClassWithArgumentsAndCallerAccessors().arguments = 456
- assert.areEqual(456, set_arguments, "new ClassWithArgumentsAndCallerAccessors().arguments = 456 (calls setter)");
- ClassWithArgumentsAndCallerAccessors.caller = 123
- assert.areEqual(123, set_caller, "ClassWithArgumentsAndCallerAccessors.caller = 123 (calls setter)");
- new ClassWithArgumentsAndCallerAccessors().caller = 456
- assert.areEqual(456, set_caller, "new ClassWithArgumentsAndCallerAccessors().caller = 456 (calls setter)");
- class ClassWithArgumentsAndCallerGeneratorMethods {
- static *arguments() { yield 'abc'; }
- static *caller() { yield 'def'; }
- *arguments() { yield '123'; }
- *caller() { yield '456'; }
- }
- let s;
- for (s of ClassWithArgumentsAndCallerGeneratorMethods.arguments()) {}
- assert.areEqual('abc', s, "s of ClassWithArgumentsAndCallerGeneratorMethods.arguments() === 'abc'");
- s;
- for (s of ClassWithArgumentsAndCallerGeneratorMethods.caller()) {}
- assert.areEqual('def', s, "s of ClassWithArgumentsAndCallerGeneratorMethods.caller() === 'def'");
- s;
- for (s of new ClassWithArgumentsAndCallerGeneratorMethods().arguments()) {}
- assert.areEqual('123', s, "s of new ClassWithArgumentsAndCallerGeneratorMethods().arguments() === '123'");
- s;
- for (s of new ClassWithArgumentsAndCallerGeneratorMethods().caller()) {}
- assert.areEqual('456', s, "s of new ClassWithArgumentsAndCallerGeneratorMethods().caller() === '456'");
- class ClassWithArgumentsAndCallerComputedNameMethods {
- static ['arguments']() { return 'abc'; }
- static ['caller']() { return 'def'; }
- ['arguments']() { return '123'; }
- ['caller']() { return '456'; }
- }
- assert.areEqual('abc', ClassWithArgumentsAndCallerComputedNameMethods.arguments(), "ClassWithArgumentsAndCallerComputedNameMethods.arguments() === 'abc'");
- assert.areEqual('def', ClassWithArgumentsAndCallerComputedNameMethods.caller(), "ClassWithArgumentsAndCallerComputedNameMethods.caller() === 'def'");
- assert.areEqual('123', new ClassWithArgumentsAndCallerComputedNameMethods().arguments(), "new ClassWithArgumentsAndCallerComputedNameMethods().arguments() === '123'");
- assert.areEqual('456', new ClassWithArgumentsAndCallerComputedNameMethods().caller(), "new ClassWithArgumentsAndCallerComputedNameMethods().caller() === '456'");
- class ClassWithArgumentsAndCallerComputedNameAccessors {
- static get ['arguments']() { return 'abc'; }
- static set ['arguments'](v) { set_arguments = v; }
- static get ['caller']() { return 'def'; }
- static set ['caller'](v) { set_caller = v; }
- get ['arguments']() { return '123'; }
- set ['arguments'](v) { set_arguments = v; }
- get ['caller']() { return '456'; }
- set ['caller'](v) { set_caller = v; }
- }
- assert.areEqual('abc', ClassWithArgumentsAndCallerAccessors.arguments, "ClassWithArgumentsAndCallerAccessors.arguments === 'abc'");
- assert.areEqual('def', ClassWithArgumentsAndCallerAccessors.caller, "ClassWithArgumentsAndCallerAccessors.caller === 'def'");
- assert.areEqual('123', new ClassWithArgumentsAndCallerAccessors().arguments, "new ClassWithArgumentsAndCallerAccessors().arguments === '123'");
- assert.areEqual('456', new ClassWithArgumentsAndCallerAccessors().caller, "new ClassWithArgumentsAndCallerAccessors().caller === '456'");
- ClassWithArgumentsAndCallerAccessors.arguments = 123
- assert.areEqual(123, set_arguments, "ClassWithArgumentsAndCallerAccessors.arguments = 123 (calls setter)");
- new ClassWithArgumentsAndCallerAccessors().arguments = 456
- assert.areEqual(456, set_arguments, "new ClassWithArgumentsAndCallerAccessors().arguments = 456 (calls setter)");
- ClassWithArgumentsAndCallerAccessors.caller = 123
- assert.areEqual(123, set_caller, "ClassWithArgumentsAndCallerAccessors.caller = 123 (calls setter)");
- new ClassWithArgumentsAndCallerAccessors().caller = 456
- assert.areEqual(456, set_caller, "new ClassWithArgumentsAndCallerAccessors().caller = 456 (calls setter)");
- class ClassWithArgumentsAndCallerComputedNameGeneratorMethods {
- static *['arguments']() { yield 'abc'; }
- static *['caller']() { yield 'def'; }
- *['arguments']() { yield '123'; }
- *['caller']() { yield '456'; }
- }
- s;
- for (s of ClassWithArgumentsAndCallerComputedNameGeneratorMethods.arguments()) {}
- assert.areEqual('abc', s, "s of ClassWithArgumentsAndCallerComputedNameGeneratorMethods.arguments() === 'abc'");
- s;
- for (s of ClassWithArgumentsAndCallerComputedNameGeneratorMethods.caller()) {}
- assert.areEqual('def', s, "s of ClassWithArgumentsAndCallerComputedNameGeneratorMethods.caller() === 'def'");
- s;
- for (s of new ClassWithArgumentsAndCallerComputedNameGeneratorMethods().arguments()) {}
- assert.areEqual('123', s, "s of new ClassWithArgumentsAndCallerComputedNameGeneratorMethods().arguments() === '123'");
- s;
- for (s of new ClassWithArgumentsAndCallerComputedNameGeneratorMethods().caller()) {}
- assert.areEqual('456', s, "s of new ClassWithArgumentsAndCallerComputedNameGeneratorMethods().caller() === '456'");
- }
- },
- {
- name: "toString on constructor should return class declaration or expression",
- body: function () {
- var B = class { };
- var A = class A extends B { constructor (...args) { super(...args); } set x(a) { this._x = a; } set y(a) { this._y = a; } };
- class C {
- set x(a) { this._x = a; }
- set y(a) { this._y = a; }
- };
- class D { constructor() {} get x() { return 0; } };
- var E = D;
- 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());
- assert.areEqual("class { }", B.prototype.constructor.toString());
- 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());
- assert.areEqual("class D { constructor() {} get x() { return 0; } }", D.prototype.constructor.toString());
- assert.areEqual("class D { constructor() {} get x() { return 0; } }", E.prototype.constructor.toString());
- }
- },
- {
- name: "class getters and setters must take exactly zero and one parameters respectively",
- body: function () {
- assert.doesNotThrow(function () { eval("class C { get foo() { } }"); }, "Class getter with zero parameters is valid syntax", "asdf");
- assert.throws(function () { eval("class C { get foo(x) { } }"); }, SyntaxError, "Class getter with one parameter is invalid syntax", "Getter functions must have no parameters");
- 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");
- assert.doesNotThrow(function () { eval("class C { set foo(x) { } }"); }, "Class setter with exactly one parameter is valid syntax", "asdf");
- assert.throws(function () { eval("class C { set foo() { } }"); }, SyntaxError, "Class setter with zero parameters is invalid syntax", "Setter functions must have exactly one parameter");
- 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");
- }
- },
- {
- name: "class identifier is const binding inside class body",
- body: function () {
- assert.throws(function () { class A { constructor() { A = 0; } }; new A(); }, TypeError, "Assignment to class identifier in constructor");
- assert.throws(function() { new (class A { constructor() { A = 0; }}); }, TypeError, "Assignment to class identifier in constructor");
- assert.throws(function() { class A { m() { A = 0; } }; new A().m(); }, TypeError, "Assignment to class identifier in method");
- assert.throws(function() { new (class A { m() { A = 0; } }).m(); }, TypeError, "Assignment to class identifier in method" );
- assert.throws(function() { class A { get x() { A = 0; } }; new A().x; }, TypeError, "Assignment to class identifier in getter");
- assert.throws(function() { (new (class A { get x() { A = 0; } })).x; }, TypeError, "Assignment to class identifier in getter");
- assert.throws(function() { class A { set x(_) { A = 0; } }; new A().x = 15; }, TypeError, "Assignment to class identifier in setter");
- assert.throws(function() { (new (class A { set x(_) { A = 0; } })).x = 15; }, TypeError, "Assignment to class identifier in setter");
- }
- },
- {
- name: "`class x extends y` where `y` is an expression containing identifier `x` should be a ReferenceError",
- body: function() {
- var refErrorText = "Use before declaration";
- function assert_referr(code) {
- assert.throws(function () { eval(code) }, ReferenceError, `\n ${code}`, refErrorText);
- }
- function assert_referrors(code) {
- // Test a given class declaration (where class is named x) by itself and then with `var x =` and `let x =` prepended.
- assert_referr(code);
- assert_referr(`var x = ${code}`);
- assert_referr(`let x = ${code}`);
- // Run the same tests, where the class declaration is a wrapped in parens to form a class expression.
- // In the Test262 test case, the RHS is a class expression.
- // See: test262/test/language/statements/class/name-binding/in-extends-expression-assigned.js
- assert_referr(`(${code})`);
- assert_referr(`var x = (${code})`);
- assert_referr(`let x = (${code})`);
- }
- function id(x) { return x; };
- function fun(x) { return function() { return x }; }
- // Using expressions containing the `x` identifier for the extends clause
- assert_referrors("class x extends x {}");
- assert_referrors("class x extends (x) {}");
- assert_referrors("class x extends id(x) {}");
- assert_referrors("class x extends fun(x) {}");
- // Assigning the result to a different identifier
- assert_referr("var y = class x extends x {}");
- assert_referr("let y = class x extends x {}");
- // Defining `y` after the class to use default initialization (var y) or temporary deadzone (let y)
- assert.throws(function() {
- class x extends y {}; // y == undefined
- var y = function() {};
- }, TypeError, "y is undefined", "Function is not a constructor");
- assert_referr(`
- class x extends y {}; // y is not defined (temporary deadzone)
- let y = function() {};
- `);
- // Using eval where the result of eval is the 'x' identifier or a value that captures the 'x' identifier
- assert_referrors("class x extends eval('x') {}");
- assert_referrors("class x extends eval('(x)') {}");
- assert_referrors("class x extends eval('id(x)') {}");
- assert_referrors("class x extends eval('fun(x)') {}");
- }
- },
- ];
- testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });
- // Bug 516429 at global scope
- class a {};
- a = null; // No error
- // Bug 257621 at global scope
- assert.doesNotThrow(function () { eval('new (class {})();'); }, "Parenthesized class expressions can be new'd");
|