blob: 27b1f43d1207ed8ccb18ad99f948526515bc177d [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: "Identifier = value shorthand",
body: function() {
var str = "prop";
assert.areEqual({ str : str }, { str } );
assert.areEqual({"b" : 123, str : str, "foo" : "bar"}, {"b" : 123, str, "foo" : "bar"});
}
},
{
name: "Shorthand names `get` and `set` parse without error (they are not keywords in these cases)",
body: function () {
var a = 0;
var get = 1;
var set = 2;
var z = 3;
var o = { get };
var p = { set };
var q = { get, set };
var r = { set, get };
var s = { get, z };
var t = { a, set };
var u = { a, get, z };
assert.areEqual(1, o.get, "o.get = 1");
assert.areEqual(2, p.set, "p.set = 2");
assert.areEqual(1, q.get, "q.get = 1");
assert.areEqual(2, q.set, "q.set = 2");
assert.areEqual(2, r.set, "r.set = 2");
assert.areEqual(1, r.get, "r.get = 1");
assert.areEqual(1, s.get, "s.get = 1");
assert.areEqual(3, s.z, "s.z = 3");
assert.areEqual(0, t.a, "t.a = 0");
assert.areEqual(2, t.set, "t.set = 2");
assert.areEqual(0, u.a, "u.a = 0");
assert.areEqual(1, u.get, "u.get = 1");
assert.areEqual(3, u.z, "u.z = 3");
}
},
{
name: "Concise method shorthand",
body: function() {
var obj = {
foo() { return "foo"; }
};
assert.areEqual("foo", obj.foo());
assert.areEqual("foo", ({ foo: function() { }, foo() { return "foo"; } }).foo());
assert.areEqual("foo", ({ foo(x) { }, foo() { return "foo"; } }).foo());
}
},
{
name: "Concise method shorthand with `get` and `set` names",
body: function () {
var o = {
get() { return "g"; },
set() { return "s"; }
};
assert.areEqual('g', o.get(), "o.get returns 'g'");
assert.areEqual('s', o.set(), "o.set returns 's'");
}
},
{
name: "Computed property names",
body: function() {
var x;
var obj = {
["foo" + "bar"] : 1,
[1 * 10 * 10] : 2,
[x = "notfoobar"] : 3,
// computed function name
["bar" + "foo"] () { return 4 },
[2 * 10 * 10] () { return 5 },
[x = "notbarfoo"] () { return 6 },
// computed get/set method name
set ["boo" + "far" ] (a) { this.x = a * 2 },
get ["boo" + "far" ] () { return this.x },
set [3 * 10 * 10] (a) { this.y = a * a },
get [3 * 10 * 10] () { return this.y },
set [x = "notboofar"] (a) { this.z = a / 3 },
get [x = "notboofar"] () { return this.z }
};
assert.areEqual(1, obj.foobar, "String concat expr as property name");
assert.areEqual(2, obj[100], "Math expr as property name");
assert.areEqual(3, obj.notfoobar, "Element list as property name");
assert.areEqual(4, obj.barfoo(), "String concat expr as method name");
assert.areEqual(5, obj[200] (), "Math expr as method name");
assert.areEqual(6, obj.notbarfoo(), "Element list as method name");
obj.boofar=7;
assert.areEqual(14, obj.boofar, "String concat expr as setter/getter method names");
obj[300]=8;
assert.areEqual(64, obj[300], "Math expr as setter/getter method names");
obj.notboofar=9;
assert.areEqual(3, obj.notboofar, "Element list as setter/getter method names");
var protoObj = {
["__proto__"] : { abc : 123 }
};
assert.areEqual(protoObj.abc, undefined, "__proto__ does not get assigned as the intrinsic proto when used as a computed property name");
var nestedProtoObj = {
["__proto__"] : {
["__" + "proto" + "__"] : {
abc : 123
}
}
};
assert.areEqual(nestedProtoObj.abc, undefined, "Nested dynamic __proto__ literals");
protoObj = {
"__proto__" : { abc : 123 }
};
assert.areEqual(protoObj.abc, 123, "__proto__ get assigned when used as a normal production");
assert.throws(function () { eval("var b = { ['str'] }"); }, SyntaxError, "Invalid computed identifier shorthand", "Expected ':'");
assert.throws(function () { eval("var b = { [1, 2]: 3 }"); }, SyntaxError, "Disallow 'Expression' inside 'ComputedPropertyName'", "Expected ']'");
}
},
{
name: "Duplicate property handling",
body: function () {
// Valid overwrite cases: old style definitions and computed property names
var obj = {
foobar : 1,
"foobar" : 2,
["foo" + "bar"] : 3,
["foo" + "bar"] : 4
}
assert.areEqual(obj.foobar, 4, "Opt-in duplicate property handling");
var obj2 = {
["foo" + "bar"] : 1,
["foo" + "bar"] : 2
}
assert.areEqual(obj2.foobar, 2, "Duplicate computed property names are allowed");
// Valid cases
var a = "str";
assert.areEqual("str", ({ a, a }).a, "Duplicate identifier references");
assert.areEqual("str", ({ 'foo' : '1', foo() { return "str"; } }).foo(), "Duplicate data property and method definition");
assert.areEqual("str", ({ set foo(x) { }, foo : "str" }).foo, "Duplicate accessors and data property");
assert.areEqual("str", ({ get foo() {}, set foo(x) { }, foo(x) { return "str"; } }).foo(), "Duplicate accessors and method definition");
assert.areEqual("a", ({ get foo() { return "str"; }, set foo(x) { }, ["foo"] : "a" }).foo, "Duplicate accessors and computed property");
}
},
{
name: "BLUE 552728: Object Literal: Use of keywords is not throwing syntax error",
body: function () {
// The following definitions ignore 'yield'
var keywords = ["break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do",
"else", "export", "extends", "finally", "for", "function", "if", "import", "in", "instanceof",
"new", "return", "super", "switch", "this", "throw", "try", "typeof", "var", "void", "while",
"with"];
var futureStrict = ["implements", "let", "private", "public", "interface", "package", "protected", "static"];
// Strict mode rules
for (var keyword in futureStrict) {
assert.throws(function () { eval("use strict; var " + keyword + " = 1; var o = { " + keyword + " };"); }, SyntaxError, keyword + " is a forbidden identifier reference");
}
// TODO (tcare): When generators are implemented, add a test case where the yield operator is used.
assert.throws(function () { eval("use strict; var yield = 1; var o = { yield }; "); }, SyntaxError);
// Non-strict mode rules
for (var keyword in keywords) {
assert.throws(function () { eval("var " + keyword + " = 1; var o = { " + keyword + " };"); }, SyntaxError, keyword + " is a forbidden identifier reference");
}
var yield = 1;
var yieldObj = { yield }; // No error
}
},
{
name: "BLUE 551475: Duplicate property definition not throwing syntax error",
body: function () {
assert.areEqual(3, ({ set b(v) { }, b : 3 }).b, "Duplicate set accessor and data property");
assert.areEqual(3, ({ get b() { }, b : 3 }).b, "Duplicate get accessor and data property");
assert.areEqual(4, ({ b : 3, get b() { return 4; } }).b, "Duplicate data property and set accessor");
}
},
{
name: "BLUE 594468: Computed properties in nested object and function return statements cause assertion",
body: function () {
() => {
return {
["a"]: null
}
}
}
},
{
name: "BLUE 563637: Computed property ordering causes crash",
body: function () {
var a = {
["a"] : 10,
b() {
return this.a;
}
};
// The implementation of computed properties causes object literals to
// be constructed (InitFld) up to the first computed property, then StElem
// for each computed property and StFld for any other non-computed properties.
// The following test cases test these transitions.
var original = {
a : 1,
b : 2,
c : 3,
d : 4,
e : 5
};
var orderOne = {
a : 1,
b : 2,
["c"] : 3,
d : 4,
e : 5
};
var orderTwo = {
a : 1,
b : 2,
c : 3,
d : 4,
["e"] : 5
};
var orderThree = {
["a"] : 1,
b : 2,
c : 3,
d : 4,
e : 5
};
var orderFour = {
["a"] : 1,
b : 2,
["c"] : 3,
d : 4,
["e"] : 5
};
assert.areEqual(original, orderOne);
assert.areEqual(original, orderTwo);
assert.areEqual(original, orderThree);
assert.areEqual(original, orderFour);
}
},
{
name: "BLUE 603997: Method formals redeclaration error",
body: function() {
assert.doesNotThrow(function() { eval("var obj = { method(a) { var a; } };"); }, "Object literal method with a var redeclaration does not throw");
assert.throws(function() { eval("var obj = { method(a) { let a; } };"); }, SyntaxError, "Object literal method with a let redeclaration throws", "Let/Const redeclaration");
assert.throws(function() { eval("var obj = { method(a) { const a; } };"); }, SyntaxError, "Object literal method with a const redeclaration throws", "Let/Const redeclaration");
assert.doesNotThrow(function() { eval("var obj = { method(a,b,c) { var b; } };"); }, "Object literal method with a var redeclaration does not throw");
assert.throws(function() { eval("var obj = { method(a,b,c) { let b; } };"); }, SyntaxError, "Object literal method with a let redeclaration throws", "Let/Const redeclaration");
assert.throws(function() { eval("var obj = { method(a,b,c) { const b; } };"); }, SyntaxError, "Object literal method with a const redeclaration throws", "Let/Const redeclaration");
assert.doesNotThrow(function() { eval("var obj = { set method(a) { var a; } };"); }, "Object literal set method with a var redeclaration does not throw");
assert.throws(function() { eval("var obj = { set method(a) { let a; } };"); }, SyntaxError, "Object literal set method with a let redeclaration throws", "Let/Const redeclaration");
assert.throws(function() { eval("var obj = { set method(a) { const a; } };"); }, SyntaxError, "Object literal set method with a const redeclaration throws", "Let/Const redeclaration");
}
},
{
name: "BLUE 618132: __proto__ after a computed property",
body: function () {
var p = { p: 123 };
var o = {
['someprop'] : 'someprop',
__proto__: p
};
assert.areEqual(p, Object.getPrototypeOf(o));
assert.isTrue(!o.hasOwnProperty("__proto__"));
assert.areEqual(123, o.p);
assert.areEqual('someprop', o.someprop);
assert.areEqual(p, Object.getPrototypeOf(o));
}
},
{
name: "BLUE 617446: Arguments identifier syntax",
body: function () {
function foo() {
var args = { arguments };
return [args.arguments[0], args.arguments[1], args.arguments.length];
}
assert.areEqual([undefined, undefined, 0], foo(), "Arguments object correctly works with identifier syntax");
assert.areEqual([-1, 1, 2], foo(-1, 1), "Arguments object correctly works with identifier syntax");
assert.areEqual([-1, 1, 3], foo(-1, 1, 0), "Arguments object correctly works with identifier syntax");
}
},
{
name: "__proto__ productions",
body: function() {
assert.throws(function() { eval("{ __proto__ : Function.prototype, __proto__ : Array.prototype }"); }, SyntaxError, "More than one regular productions can't define __proto__");
var __proto__ = {};
assert.throws(function() { eval("var o = { __proto__ : Function.prototype, __proto__, __proto__ : Array.prototype };"); }, SyntaxError, "More than one regular productions can't define __proto__ even if there are other productions present");
assert.isTrue({ __proto__, __proto__ : [], __proto__() {}, __proto__ } instanceof Array, "Regular production model should win over all other");
assert.isTrue({ ['__proto__'] : Object.prototype, __proto__ : [], ['__proto__'] : {} } instanceof Array, "Computed property definition of __proto__ shouldn't override the regular production");
assert.isTrue({ __proto__ : [] } instanceof Array, "Regular production for __proto__ should set the internal prototype");
assert.areEqual(Object.getPrototypeOf({ __proto__ : null }), null, "Null should be set as the prototype when specified using normal production");
assert.areEqual(Object.getPrototypeOf({ __proto__ : undefined }), Object.prototype, "Undefined should not be set as the internal prototype for object literal");
assert.areEqual(Object.getPrototypeOf({ __proto__ : "a" }), Object.prototype, "Non-object type string shouldn't be set as the internal prototype for object literal");
assert.areEqual(Object.getPrototypeOf({ __proto__ : 10 }), Object.prototype, "Non-object type number shouldn't be set as the internal prototype for object literal");
assert.areEqual(Object.getPrototypeOf({ __proto__ : true }), Object.prototype, "Non-object type boolean shouldn't be set as the internal prototype for object literal");
var str = "__proto__";
assert.isFalse({ [str] : [] } instanceof Array, "Computed property shouldn't set the internal prototype");
__proto__ = [];
assert.isFalse({ __proto__ } instanceof Array, "Identifier reference shouldn't set the internal prototype");
assert.isFalse({__proto__() {}} instanceof Function, "Method definition shouldn't set the internal prototype");
function f() {}
var obj = { "__proto__" : [], ["__proto__"] : f.prototype };
Array.prototype.x = 1;
f.prototype.x = 10;
assert.areEqual(obj.x, 1, "Regular production should assign the internal prototype");
assert.areEqual(obj.__proto__.x, 10, "Computed property definition of __proto__ is added as a data member");
}
},
{
name: "computed property getters can call super methods",
body: function () {
function ID(x) { return x; }
var proto = {
m() { return ' proto m'; }
};
var object = {
get ['a']() { return 'a' + super.m(); },
get [ID('b')]() { return 'b' + super.m(); },
get [0]() { return '0' + super.m(); },
get [ID(1)]() { return '1' + super.m(); },
};
Object.setPrototypeOf(object, proto);
assert.areEqual('a proto m', object.a, "The value of `object.a` is `'a proto m'`. Defined as `get ['a']() { return 'a' + super.m(); }`");
assert.areEqual('b proto m', object.b, "The value of `object.a` is `'b proto m'`. Defined as `get [ID('b')]() { return 'b' + super.m(); }`");
assert.areEqual('0 proto m', object[0], "The value of `object[0]` is `'0 proto m'`. Defined as `get [0]() { return '0' + super.m(); }`");
assert.areEqual('1 proto m', object[1], "The value of `object[1]` is `'1 proto m'`. Defined as `get [ID(1)]() { return '1' + super.m(); }`");
}
},
{
name: "computed property setters can call super methods",
body: function () {
function ID(x) {
return x;
}
var value;
var proto = {
m(name, v) { value = name + ' ' + v; }
};
var object = {
set ['a'](v) { super.m('a', v); },
set [ID('b')](v) { super.m('b', v); },
set [0](v) { super.m('0', v); },
set [ID(1)](v) { super.m('1', v); },
};
Object.setPrototypeOf(object, proto);
object.a = 2;
assert.areEqual('a 2', value, "The value of `value` is `'a 2'`, after executing `object.a = 2;`");
object.b = 3;
assert.areEqual('b 3', value, "The value of `value` is `'b 3'`, after executing `object.b = 3;`");
object[0] = 4;
assert.areEqual('0 4', value, "The value of `value` is `'0 4'`, after executing `object[0] = 4;`");
object[1] = 5;
assert.areEqual('1 5', value, "The value of `value` is `'1 5'`, after executing `object[1] = 5;`");
}
},
{
name: "computed property methods can call super methods",
body: function () {
function ID(x) { return x; }
var proto = {
m() { return ' proto m'; }
};
var object = {
['a']() { return 'a' + super.m(); },
[ID('b')]() { return 'b' + super.m(); },
[0]() { return '0' + super.m(); },
[ID(1)]() { return '1' + super.m(); },
};
Object.setPrototypeOf(object, proto);
assert.areEqual('a proto m', object.a(), "`object.a()` returns `'a proto m'`, after executing `Object.setPrototypeOf(object, proto);`");
assert.areEqual('b proto m', object.b(), "`object.b()` returns `'b proto m'`, after executing `Object.setPrototypeOf(object, proto);`");
assert.areEqual('0 proto m', object[0](), "`object[0]()` returns `'0 proto m'`, after executing `Object.setPrototypeOf(object, proto);`");
assert.areEqual('1 proto m', object[1](), "`object[1]()` returns `'1 proto m'`, after executing `Object.setPrototypeOf(object, proto);`");
}
},
{
name: "super method calls in object literal method",
body: function () {
var proto = {
method(x) { return 'proto' + x; }
};
var object = {
method(x) { return super.method(x); }
};
Object.setPrototypeOf(object, proto);
assert.areEqual('proto42', object.method(42), "`object.method(42)` returns `'proto42'`, after executing `Object.setPrototypeOf(object, proto);`");
assert.areEqual('proto42', proto.method(42), "`proto.method(42)` returns `'proto42'`, after executing `Object.setPrototypeOf(object, proto);`");
assert.areEqual('proto42', Object.getPrototypeOf(object).method(42), "`Object.getPrototypeOf(object).method(42)` returns `'proto42'`");
}
},
{
name: "super method calls in object literal getter",
body: function () {
var proto = {
_x: 42,
get x() { return 'proto' + this._x; }
};
var object = {
get x() { return super.x; }
};
Object.setPrototypeOf(object, proto);
assert.areEqual('proto42', object.x, "The value of `object.x` is `'proto42'`, after executing `Object.setPrototypeOf(object, proto);`");
assert.areEqual(42, object._x, "The value of `object._x` is `42`, after executing `Object.setPrototypeOf(object, proto);`");
assert.areEqual(42, Object.getPrototypeOf(object)._x, "The value of `Object.getPrototypeOf(object)._x` is `42`");
}
},
{
name: "super method calls in object literal setter",
body: function () {
var proto = {
_x: 0,
set x(v) { return this._x = v; }
};
var object = {
set x(v) { super.x = v; }
};
Object.setPrototypeOf(object, proto);
assert.areEqual(1, object.x = 1, "`object.x = 1` is `1`, after executing `Object.setPrototypeOf(object, proto);`");
assert.areEqual(1, object._x, "The value of `object._x` is `1`, after executing `Object.setPrototypeOf(object, proto);`");
assert.areEqual(0, Object.getPrototypeOf(object)._x, "The value of `Object.getPrototypeOf(object)._x` is `0`");
}
},
{
name: "The HomeObject of Functions declared as methods is the Object prototype.",
body: function () {
var obj = {
method() { return super.toString; }
};
obj.toString = null;
assert.areEqual(Object.prototype.toString, obj.method());
}
},
{
name: "The HomeObject accessed through default argument of Functions declared as methods is the Object prototype.",
body: function () {
var obj = {
method(x = super.toString) { return x; }
};
obj.toString = null;
assert.areEqual(Object.prototype.toString, obj.method());
}
},
];
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });