| //------------------------------------------------------------------------------------------------------- |
| // 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"); |
| |
| var tests = [ |
| { |
| name: "GitHub ChakraCore #1465 - call syntax is allowed after class expression", |
| body: function () { |
| var s1 = class { }.toString(); |
| var s2 = class x { }.toString(); |
| var s3 = class x { }.toString(1, 2, 3); // arguments should not affect valid parse |
| |
| assert.areEqual("class { }", s1, "Calling toString after a class expression with no name parses and behaves correctly"); |
| assert.areEqual("class x { }", s2, "Calling toString after a class expression with a name parses and behaves correctly"); |
| assert.areEqual("class x { }", s3, "Calling toString with arguments after a class expression with a name parses and behaves correctly"); |
| } |
| }, |
| { |
| name: "BLUE 540289: AV on deferred parse of first class method", |
| body: function () { |
| assert.throws(function() { eval("function f() { var o = { \"a\": class { \"b\""); }, SyntaxError); |
| } |
| }, |
| { |
| name: "BLUE 558906: [ES6][Class] get and set should be valid method names", |
| body: function () { |
| class foo { |
| set(key) { } // No error |
| get() { } // No error |
| } |
| } |
| }, |
| { |
| name: "BLUE 573391: Classes extending null with a non-default constructor crash", |
| body: function () { |
| class A { constructor() { } }; |
| var test1 = class { constructor(args) { } }; |
| var test2 = class extends null { constructor(args) { } }; |
| var test3 = class extends A { constructor(args) { } }; |
| var test4 = class extends A { constructor(args) { super(args) } }; |
| } |
| }, |
| { |
| name: "BLUE 603997: Method formals redeclaration error", |
| body: function() { |
| assert.throws(function() { eval("class { method(a) { var a; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| assert.throws(function() { eval("class { method(a) { let a; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| assert.throws(function() { eval("class { method(a) { const a; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| |
| assert.throws(function() { eval("class { method(a,b,c) { var b; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| assert.throws(function() { eval("class { method(a,b,c) { let b; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| assert.throws(function() { eval("class { method(a,b,c) { const b; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| |
| assert.throws(function() { eval("class { set method(a) { var a; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| assert.throws(function() { eval("class { set method(a) { let a; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| assert.throws(function() { eval("class { set method(a) { const a; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| |
| assert.throws(function() { eval("class { set method(a,b,c) { var b; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| assert.throws(function() { eval("class { set method(a,b,c) { let b; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| assert.throws(function() { eval("class { set method(a,b,c) { const b; }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| |
| assert.throws(function() { eval("class { method(a,a,c) { }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| assert.throws(function() { eval("class { set method(a,a,c) { }; }"); }, SyntaxError, "Method formal parameters cannot be redeclared."); |
| } |
| }, |
| { |
| name: "BLUE 629214: Class methods with a prefix crash in deferred parse", |
| body: function () { |
| function test1() { class a { static "a"() { } } } |
| function test2() { class a { static get "a"() { } } } |
| function test3() { class a { static set "a"(x) { } } } |
| function test4() { class a { get "a"() { } } } |
| function test5() { class a { set "a"(x) { } } } |
| function test6() { class a { *"a"(x) { } } } |
| function test7() { class a { method() {} "a"() {} } } |
| function test8() { class a { method() {} static "a"() { } } } |
| function test9() { class a { method() {} static get "a"() { } } } |
| function test10() { class a { method() {} static set "a"(x) { } } } |
| function test11() { class a { method() {} get "a"() { } } } |
| function test12() { class a { method() {} set "a"(x) { } } } |
| function test13() { class a { method() {} *"a"(x) { } } } |
| } |
| }, |
| { |
| name: "OS 102456: Assert when deleting a non-method property from a class", |
| body: function () { |
| u3056 = function() {}; |
| class c extends u3056 {}; |
| c.y = "str"; |
| delete c.x; |
| delete c.y; |
| } |
| }, |
| { |
| name: "OS 112921: Nested evals attempt to load super into a scope slot", |
| body: function () { |
| class z{window(){((eval("")))((this))}}; |
| eval("class z{window(){((eval(\"\")))((this))}};"); |
| } |
| }, |
| { |
| name: "OS 101184: Class methods without separators inside an array break deferred parsing heuristics", |
| body: function () { |
| Function("[class z{\u3056(){}functional(){}}]"); |
| } |
| }, |
| { |
| name: "OS 182090: Class method after a semicolon terminated method does not force PID", |
| body: function () { |
| z = (class { |
| if (shouldBailout) { /*bLoop*/ }; |
| "" (x) {} |
| }) |
| } |
| }, |
| { |
| name: "OS 257621: Class expressions should not have trailing call parens", |
| body: function () { |
| assert.throws(function () { eval('class{}();'); }, SyntaxError, "Class expressions cannot be called without parens", "Expected identifier"); |
| assert.doesNotThrow(function () { eval('new (class {})();'); }, "Parenthesized class expressions can be called"); |
| } |
| }, |
| { |
| name: "OS 1114090: Getters in superclass should use instance of subclass as actual 'this' object", |
| body: function () { |
| |
| class Person { |
| getFullName() { |
| return this.firstName + " " + this.lastName; |
| } |
| get fullName() { |
| return this.firstName + " " + this.lastName; |
| } |
| } |
| |
| class MedicalWorker extends Person { } // to show it works through inheritance chain |
| |
| class Doctor extends MedicalWorker { |
| constructor(firstName,lastName) { |
| super(); |
| this.firstName = firstName; |
| this.lastName = lastName; |
| } |
| getFullNameExplicit() { return "Dr. " + super.getFullName(); } |
| getFullNameProperty() { return "Dr. " + super.fullName; } |
| getFullNameEvalCall() { return "Dr. " + eval('super.getFullName()'); } |
| getFullNameEvalProperty() { return "Dr. " + eval('super.fullName'); } |
| getFullNameLambdaCall() { return "Dr. " + (()=>super.getFullName()) (); } |
| getFullNameLambdaProperty() { return "Dr. " + (()=>super.fullName) (); } |
| } |
| |
| let x = new Doctor("John","Smith"); |
| assert.areEqual("Dr. John Smith", x.getFullNameExplicit(), "explicit super call should use instance of subclass as actual 'this' object"); |
| assert.areEqual("Dr. John Smith", x.getFullNameProperty(), "property accessor in superclass should use instance of subclass as actual 'this' object"); |
| assert.areEqual("Dr. John Smith", x.getFullNameEvalCall(), "super called from within eval should have same behavior as outside of eval"); |
| assert.areEqual("Dr. John Smith", x.getFullNameEvalProperty(), "super object property access from within eval should have same behavior as outside of eval"); |
| assert.areEqual("Dr. John Smith", x.getFullNameLambdaCall(), "super called from within lambda should have same behavior as outside of lambda"); |
| assert.areEqual("Dr. John Smith", x.getFullNameLambdaProperty(), "super object property access from within lambda should have same behavior as outside of lambda"); |
| } |
| }, |
| { |
| name: "OS 4586602: Setters in superclass should use instance of subclass as actual 'this' object", |
| body: function () { |
| |
| // test case from OS4586602 |
| |
| class Proto { |
| set x(v) { return this._x = v; } |
| }; |
| |
| class Obj extends Proto { |
| set x(v) { super.x = v; } |
| }; |
| |
| var object = new Obj(); |
| object.x=1; |
| assert.areEqual(1, object._x, "setters in superclass should use instance of subclass as actual 'this' object"); |
| |
| // behavior according to ECMA2015 when superclass accessors are not present |
| |
| class A { } |
| |
| class B extends A { |
| getA() { return super.i; } |
| setA(v) { super.i = v; } |
| } |
| |
| let b = new B(); |
| b.setA(15); |
| |
| assert.areEqual(true, b.hasOwnProperty('i'), "Property 'i' should exist in actualThis object(b)"); |
| assert.areEqual(15, b.i, "Property 'i' should match assigned value in actualThis object(b)"); |
| assert.areEqual(false, A.prototype.hasOwnProperty('i'), "Property 'i' should not exist in base object(A.prototype)"); |
| assert.areEqual(undefined, b.getA(), "Property 'i' should be undefined in base object(A.prototype)"); |
| |
| // other cases similar to getter tests above |
| |
| class Base { |
| setName(v) { this._name = v; } |
| set name(v) { this._name = v; } |
| } |
| |
| class Middle extends Base { } // to show it works through inheritance chain |
| |
| class Subclass extends Middle { |
| setNameExplicit(name) { super.setName(name); } |
| setNameProperty(name) { super.name=name; } |
| setNameEvalCall(name) { eval('super.setName(name)'); } |
| setNameEvalProperty(name) { eval('super.name=name'); } |
| setNameLambdaCall(name) { (()=>super.setName(name)) (); } |
| setNameLambdaProperty(name) { (()=>super.name=name) (); } |
| } |
| |
| let x = new Subclass(); |
| x.setNameExplicit("explicit"); |
| assert.areEqual("explicit", x._name, "explicit super call should use instance of subclass as actual 'this' object"); |
| x.setNameProperty("property"); |
| assert.areEqual("property", x._name, "property accessor in superclass should use instance of subclass as actual 'this' object"); |
| x.setNameEvalCall("evalCall"); |
| assert.areEqual("evalCall", x._name, "super called from within eval should have same behavior as outside of eval"); |
| x.setNameEvalProperty("evalProperty"); |
| assert.areEqual("evalProperty", x._name, "super object property access from within eval should have same behavior as outside of eval"); |
| x.setNameLambdaCall("lambdaCall"); |
| assert.areEqual("lambdaCall", x._name, "super called from within lambda should have same behavior as outside of lambda"); |
| x.setNameLambdaProperty("lambdaProperty"); |
| assert.areEqual("lambdaProperty", x._name, "super object property access from within lambda should have same behavior as outside of lambda"); |
| } |
| }, |
| { |
| name: "OS 1001915: Function built-in properties \'length\', \'caller\', \'arguments\' should not shadow class members", |
| body: function () { |
| class A { |
| static length() { } |
| static caller() { } |
| static arguments() { } |
| }; |
| assert.areEqual("length() { }", A.length.toString(), "Accessing static method \'length\'"); |
| assert.areEqual("caller() { }", A.caller.toString(), "Accessing static method \'caller\'"); |
| assert.areEqual("arguments() { }", A.arguments.toString(), "Accessing static method \'arguments\'"); |
| for (var p in A) { |
| assert.areEqual(p+"() { }", A[p].toString(), "PropertyString for \'"+p+"\' should have a matching cached value"); |
| } |
| |
| class B { |
| set length(a) { this._length=a; } |
| get length() { return this._length; } |
| set caller(a) { this._caller=a; } |
| get caller() { return this._caller; } |
| set arguments(a) { this._arguments=a; } |
| get arguments() { return this._arguments; } |
| }; |
| var b=new B(); |
| b.length=100; |
| b.caller="Caller"; |
| b.arguments=function() { }; |
| assert.areEqual(100, b.length, "Get/set accessor \'length\'"); |
| assert.areEqual("Caller", b.caller, "Get/set accessor \'caller\'"); |
| assert.areEqual("function () { }", b.arguments.toString(), "Get/set accessor \'arguments\'"); |
| } |
| }, |
| { |
| name: "OS4497562,OS4497908: InitClassMember overriding existing accessor property", |
| body: function () { |
| class A { |
| set z(v) {} |
| z() {} // bug repro: ASSERT(jscript\core\lib\Runtime\Types\DictionaryTypeHandler.cpp, line 1737) Expect to only come down this path for ... |
| }; |
| |
| class B { |
| static set z(v) {} |
| static z() {} // bug repro: ASSERT(jscript\core\lib\Runtime\Types\DictionaryTypeHandler.cpp, line 1737) Expect to only come down this path for ... |
| }; |
| |
| class C { |
| set z(v) {} |
| *z() {} // bug repro: ASSERT(jscript\core\lib\Runtime\Types\DictionaryTypeHandler.cpp, line 1737) Expect to only come down this path for ... |
| }; |
| |
| class D { |
| set z(v) {} |
| ["z"]() {} // bug repro: ASSERT(jscript\core\lib\Runtime\Types\DictionaryTypeHandler.cpp, line 1737) Expect to only come down this path for ... |
| }; |
| } |
| }, |
| { |
| name: "OS4352944: ES6 class super.<method> calls in an eval inside constructor", |
| body: function () { |
| var count = 0; |
| class A { |
| constructor() { count++; } |
| increment() { count++; } |
| decrement() { count--; } |
| getCount() { return count; } |
| } |
| |
| class B extends A { |
| constructor() { |
| eval(" \ |
| super(); \ |
| assert.areEqual(1,super.getCount()); \ |
| super.increment(); \ |
| assert.areEqual(2, super.getCount()); \ |
| super.decrement(); \ |
| assert.areEqual(1, super.getCount()); \ |
| "); |
| } |
| } |
| var bar = new B(); |
| } |
| }, |
| { |
| name: "OS6135311: Incorrect base register assignment in inlined LdNewTarget", |
| body: function () { |
| function func5 () { |
| assert.areEqual(class0, new.target, "new.target should return subclass constructor for super constructor call"); |
| function v0() {}; |
| } |
| class class0 extends func5 { } |
| |
| new class0(); |
| new class0(-1); |
| new class0([2,3], NaN); |
| new class0("cat", 100, {}); |
| } |
| }, |
| ]; |
| |
| 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"); |