blob: 6120c0ec8f10d61fc2ffa99d101a78dd428f57e3 [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// 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");