blob: 0b0b0a20595aed4e39f2c31ecdfbc623909364c8 [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.
//-------------------------------------------------------------------------------------------------------
if (typeof(WScript) != "undefined") {
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
}
var tests = {
// Note: each test has name (string) and body (function) properties.
// Success is when the body does not throw, failure -- when it throws.
test01: {
name: "formal arg: simple: verify connection: named vs indexed arg",
body: function () {
var passedValue = 1;
function f(a) {
var val1 = 2;
a = val1;
assert.areEqual(val1, a, "wrong value of named parameter (val1)");
assert.areEqual(val1, arguments[0], "wrong value of indexed parameter (val1)");
var val2 = 3;
arguments[0] = val2;
assert.areEqual(val2, arguments[0], "wrong value of indexed parameter (val2)");
assert.areEqual(val2, a, "wrong value of named parameter (val2)");
}
f(passedValue);
}
},
test02: {
name: "formal arg: defineProperty, check property descriptor",
body: function () {
var passedValue = 1;
function f(a) {
var val = 2;
Object.defineProperty(arguments, 0, { configurable: false, enumerable: false, value: val });
// Note that we expect writable: true because this was omitted in defineProperty above
// which is actually re-define property with all attributes == true.
var expected = { configurable: false, enumerable: false, writable: true, value: val };
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0), "wrong value of getOwnPropertyDescriptor");
assert.areEqual(val, a, "wrong value of named parameter");
}
f(passedValue);
}
},
test03: {
name: "formal arg: defineProperty, set writable to false, verify writability and lost connection. WOOB 1128023",
body: function () {
var passedValue = 1;
function f(a) {
Object.defineProperty(arguments, 0, { writable: false });
var expected = { configurable: true, enumerable: true, writable: false, value: passedValue };
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0), "wrong value of getOwnPropertyDescriptor");
// Attempt to change arguments[0] which is not writable now.
var val1 = 2;
arguments[0] = val1;
assert.areEqual(passedValue, arguments[0], "non-writable changed");
assert.areEqual(passedValue, a, "non-writable changed: named arg also changed");
// Change named arg value, verify we are in connection named vs indexed arg.
var val2 = 3;
a = val2;
assert.areEqual(val2, a, "Attemp to change named arg: didn't work");
assert.areEqual(passedValue, arguments[0], "At this time we should not be connected, but we are");
}
f(passedValue);
}
},
test04: {
name: "formal arg: defineProperty, set writable to false AND set value, verify that value changed in both named and indexed arg and that the item was disconnected",
body: function () {
var passedValue = 1;
function f(a) {
var val1 = 2;
var val2 = 3;
Object.defineProperty(arguments, 0, { writable: false, value: val1 });
var expected = { configurable: true, enumerable: true, writable: false, value: val1 };
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0), "wrong value of getOwnPropertyDescriptor");
assert.areEqual(val1, arguments[0], "value: arguments[0]");
assert.areEqual(val1, a, "value: a");
// Verify we are disconnected now.
a = val2;
assert.areEqual(val2, a, "new value: a");
assert.areEqual(val1, arguments[0], "value: arguments[0] -- did not get disconnected!");
}
f(passedValue);
}
},
test05: {
name: "formal arg: defineProperty w/o cause of disconnect, verify still connected to named arg",
body: function () {
var passedValue = 1;
var val1 = 2;
var val2 = 3;
function f(a) {
Object.defineProperty(arguments, 0, { value: val1 });
a = val1;
assert.areEqual(val1, arguments[0], "arguments[0] got disconnected");
arguments[0] = val2;
assert.areEqual(val2, a, "a got disconnected");
}
f(passedValue);
}
},
test06: {
name: "formal arg: defineProperty, disconnect arg[0], verify that arg[1] is still connected",
body: function () {
function f(a, b) {
Object.defineProperty(arguments, 0, { writable: false });
var val1 = 3;
var val2 = 4;
arguments[1] = val1;
assert.areEqual(val1, b, "arg[1] got disconnected");
b = val2;
assert.areEqual(val2, arguments[1], "arg[1] got disconnected");
}
f(1, 2);
}
},
test07: {
name: "formal arg: defineProperty: convert to accessor property",
body: function () {
function f(a) {
var isGetterFired = false;
var isSetterFired = false;
Object.defineProperty(arguments, 0, {
get: function() { isGetterFired = true; return this.value; },
set: function(arg) { isSetterFired = true; this.value = arg; }
});
assert.areEqual(undefined, arguments[0], "unexpected arg[0] value right after conversion to accessor property");
assert.areEqual(true, isGetterFired, "isGetterFired (1)");
isGetterFired = false;
var val1 = 2;
arguments[0] = val1;
assert.areEqual(true, isSetterFired, "isSetterFired");
assert.areEqual(val1, arguments[0], "get value after set");
assert.areEqual(true, isGetterFired, "isGetterFired (2)");
}
f(1);
}
},
test08: {
name: "formal arg: defineProperty: convert to accessor, then to data property, verify value and that connection is lost",
body: function () {
var passedValue = 1;
var val1 = 2;
var val2 = 3;
function f(a) {
Object.defineProperty(arguments, 0, {
get: function() { return this.value; },
set: function(arg) { this.value = arg; }
});
Object.defineProperty(arguments, 0, { value: val1 });
a = val2;
assert.areEqual(arguments[0], val1, "arguments[0]");
assert.areNotEqual(arguments[0], a, "arguments[0] != a");
}
f(passedValue);
}
},
test09: {
name: "formal arg: defineProperty: force convert to ES5 version but keep connected, check enumeration",
body: function () {
var passedValue = 1;
function f(a) {
Object.defineProperty(arguments, 0, { enumerable: true });
var accumulator = "";
for (var i in arguments) {
accumulator += i.toString() + ": " + arguments[i] + ";";
}
assert.areEqual("0: " + passedValue + ";" , accumulator, "accumulator");
}
f(passedValue);
}
},
test10: {
name: "formal arg: defineProperty: set non-enumerable/non-writable/delete, check enumeration",
body: function () {
var passedValue1 = 2;
var passedValue2 = 4;
function f(a, b, c, d) {
Object.defineProperty(arguments, 0, { enumerable: false }); // arguments[0].enumerable = false.
Object.defineProperty(arguments, 1, { writable: false }); // arguments[1].writable = false -> disconnected.
delete arguments[2]; // arguments[2] is deleted.
var i, accumulator = "";
for (i in arguments) {
accumulator += i.toString() + ": " + arguments[i] + ";";
}
// Note that we expect [1].enumerable = true because this was omitted in defineProperty above
// which is actually re-define property that previously already had enumerable = true.
assert.areEqual("1: " + passedValue1 + ";" + "3: " + passedValue2 + ";", accumulator, "accumulator");
}
f(1, passedValue1, 3, passedValue2);
}
},
test11: {
name: "passed/undeclared arg: verify there is no correlation with Object.prototype indexed data properties. WOOB 1143896",
body: function () {
var passedValue = "passed";
Object.defineProperty(Object.prototype, 0, { value: "from proto", configurable: true, writable: false });
try {
function f() { return arguments; }
var argObj = f(passedValue);
assert.areEqual(passedValue, argObj[0]);
} finally {
delete Object.prototype[0];
}
}
},
test12: {
name: "formal arg: verify there is no correlation with Object.prototype indexed properties",
body: function () {
var passedValue = "passed";
Object.defineProperty(Object.prototype, 0, { value: "from proto", configurable: true, writable: false });
try {
function f(a) { return arguments }
var argObj = f(passedValue);
assert.areEqual(passedValue, argObj[0]);
} finally {
delete Object.prototype[0];
}
}
},
test13: {
name: "passed/undeclared arg: verify there is no correlation with Object.prototype indexed accessor properties. WOOB 1144602",
body: function () {
var initial = "initial";
var passedValue = "passed";
var data = initial;
Object.defineProperty(Object.prototype, 0, {
configurable: true,
get: function() { return data; },
set: function(arg) { data = arg; }
});
try {
function f() { return arguments; }
var argObj = f(passedValue);
assert.areEqual(initial, data, "data: should not be changed as setter on prototype should not be fired");
assert.areEqual(passedValue, argObj[0], "argObj[0]");
} finally {
delete Object.prototype[0];
}
}
},
test14: {
name: "formal arg: verify there is no correlation with Object.prototype indexed accessor properties",
body: function () {
var initial = "initial";
var passedValue = "passed";
var data = initial;
Object.defineProperty(Object.prototype, 0, {
configurable: true,
get: function() { return data; },
set: function(arg) { data = arg; }
});
try {
function f(a) { return arguments; }
var argObj = f(passedValue);
assert.areEqual(initial, data, "data: should not be changed as setter on prototype should not be fired");
assert.areEqual(passedValue, argObj[0], "argObj[0]");
} finally {
delete Object.prototype[0];
}
}
},
test15: {
name: "formal arg: delete, make sure it's deleted",
body: function () {
var passedValue = 1;
function f(a) {
Object.defineProperty(arguments, 0, { enumerable: false }); // Force convert to ES5 version.
delete arguments[0];
assert.areEqual(undefined, arguments[0], "was not deleted.");
assert.areEqual(passedValue, a, "a is changed.");
}
f(passedValue);
}
},
test16: {
name: "formal arg: delete, add, check named arg is not changed",
body: function () {
var passedValue = 1;
function f(a, b) {
Object.defineProperty(arguments, 0, { enumerable: false }); // Force convert to ES5 version.
delete arguments[0];
arguments[0] = passedValue + 1;
assert.areEqual(passedValue, a, "a is changed.");
}
f(passedValue, 2);
}
},
test17: {
name: "formal arg: delete, then defineProperty with attributes for data property, check the value",
body: function () {
var passedValue = 1;
function f(a) {
delete arguments[0];
var val = 2;
Object.defineProperty(arguments, 0, { enumerable: true, configurable: true, writable: true, value: val });
assert.areEqual(val, arguments[0], "wrong value");
}
f(passedValue);
}
},
test18: {
name: "formal arg: delete, then defineProperty with attributes for accessor property, check the enumeration",
body: function () {
var passedValue = 1;
var getter = function() {return this.value; };
var setter = function(arg) { this.value = arg; };
function f(a) {
delete arguments[0];
Object.defineProperty(arguments, 0, { enumerable: true, configurable: true, get: getter, set: setter });
var expected = { configurable: true, enumerable: true, get: getter, set: setter };
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0), "wrong descriptor");
var accumulator = "";
for (var i in arguments) {
accumulator += i.toString() + ": " + arguments[i] + ";";
}
assert.areEqual("0: " + undefined + ";", accumulator, "accumulator 2");
}
f(passedValue);
}
},
test19: {
name: "formal arg, es5 heap arguments: delete, add, check enumerable/order",
body: function () {
var passedValue1 = 1;
var passedValue2 = 2;
var newValue1 = 100;
var newValue2 = 200;
var i, accumulator;
function f(a, b) {
// Scenario 1: delete prior to converting to ES5 version.
delete arguments[0]; // Delete [0] prior to conversion to ES5.
Object.defineProperty(arguments, 0, { configurable: true, enumerable: true, value: newValue1 }); // Bring back [0] by defineProperty. Now args is ES5.
accumulator = "";
for (i in arguments) {
accumulator += i.toString() + ": " + arguments[i] + ";";
}
assert.areEqual("0: " + newValue1 + ";" + "1: " + passedValue2 + ";", accumulator, "accumulator 1");
// Scenario 2: delete after converting to ES5 version.
Object.defineProperty(arguments, 0, { configurable: true, enumerable: true, writable: true, value: newValue1 }); // Bring back [0] by defineProperty. Now args is ES5.
delete arguments[0]; // Delete [0] prior after conversion to ES5.
arguments[0] = newValue2; // Bring back [0] by setting value.
accumulator = "";
for (i in arguments) {
accumulator += i.toString() + ": " + arguments[i] + ";";
}
assert.areEqual("0: " + newValue2 + ";" + "1: " + passedValue2 + ";", accumulator, "accumulator 2");
}
f(passedValue1, passedValue2);
}
},
test20: {
name: "formal arg, es5 heap arguments: delete, add, keep another arg in objectArray and use one non-formal, check enumerable/order",
body: function () {
var passedValue1 = 1;
var passedValue2 = 2;
var passedValue3 = 3;
var passedValue4 = 4;
var newValue = 100;
function f(a, b, c) {
Object.defineProperty(arguments, 0, { enumerable: true }); // Add objectArray item
Object.defineProperty(arguments, 2, { enumerable: true }); // Add objectArray item
var accumulator = "";
delete arguments[0];
arguments[0] = newValue;
for (var i in arguments) {
accumulator += i.toString() + ": " + arguments[i] + ";";
}
assert.areEqual(
"0: " + newValue + ";" + "1: " + passedValue2 + ";" + "2: " + passedValue3 + ";" + "3: " + passedValue4 + ";",
accumulator,
"accumulator");
}
f(passedValue1, passedValue2, passedValue3, passedValue4);
}
},
test21: {
name: "formal arg: defineProperty, set enumerable to false, check getOwnPropertyNames",
body: function (a, b) {
function f(a) {
Object.defineProperty(arguments, 0, { enumerable: false });
// Note: Object.getOwnPropertyNames returns all properties, even non-enumerable.
var actual = Object.getOwnPropertyNames(arguments);
var expected = { 0: "0", 1: "1", 2: "length", 3: "callee" };
assert.areEqual(expected, actual, "wrong property names");
}
f(101, 102);
}
},
test22Helper: function test22Helper(isConvertNeeded, messagePrefix) {
function mkerr(message) {
return messagePrefix + ": " + message;
}
var passedValue = 1;
var newPropertyName = "x";
function f(a, b) {
if (isConvertNeeded) {
Object.defineProperty(arguments, 1, { enumerable: true }); // Force convert to ES5 version.
}
Object.preventExtensions(arguments); // No new properties can be added.
assert.areEqual(false, Object.isExtensible(arguments), mkerr("isExtensible"));
try {
Object.defineProperty(arguments, newPropertyName, { enumerable: true, value: 100 }); // add new property
assert.fail(mkerr("did not throw exception"));
} catch (ex) {
}
arguments[newPropertyName] = 100;
assert.areEqual(undefined, arguments[newPropertyName], mkerr("New property was added after preventExtensions was called"));
}
f(passedValue, passedValue + 1);
},
test22_1: {
name: "arguments (non-ES5 version): call Object.preventExtensions, try add new property by defineProperty and direct set",
body: function () {
tests.test22Helper(false, "non-ES5 version");
}
},
test22_2: {
name: "arguments (ES5 version): call Object.preventExtensions, try add new property by defineProperty and direct set",
body: function () {
tests.test22Helper(true, "ES5 version");
}
},
test23Helper: function test23Helper(isConvertNeeded, messagePrefix) {
function mkerr(message) {
return messagePrefix + ": " + message;
}
var passedValue = 1;
function f(a, b) {
if (isConvertNeeded) {
Object.defineProperty(arguments, 1, { enumerable: true }); // Force convert to ES5 version.
}
Object.preventExtensions(arguments); // This causes configurable, writable = false for all properties + Object.preventExtensions.
// Note: formals existed prior to calling Object.preventExtensions, thus they are still modifiable.
assert.areEqual(false, Object.isExtensible(arguments), "isExtensible");
var actual = Object.getOwnPropertyDescriptor(arguments, 0);
var expected = { configurable: true, enumerable: true, writable: true, value: passedValue };
assert.areEqual(expected, actual, mkerr("wrong descriptor - initial"));
// Try to modify/re-configure
// Note: do not change value here as it causes different code path than exercised by identified issue.
Object.defineProperty(arguments, 0, { enumerable: false });
Object.defineProperty(arguments, 0, { writable: false });
Object.defineProperty(arguments, 0, { configurable: false });
var expected = { configurable: false, enumerable: false, writable: false, value: passedValue };
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0), mkerr("wrong descriptor - after redefine"));
}
f(passedValue, passedValue + 1);
},
// After Object.preventExtensions(arguments) we can't modify the attributes on formals.
test23_1: {
name: "arguments (non-ES5 version): call Object.preventExtensions, make sure we can still modify atttibutes on formals without changing the value",
body: function () {
tests.test23Helper(false, "non-ES5 version");
}
},
// After Object.preventExtensions(arguments) we can't modify the attributes on formals.
test23_2: {
name: "arguments (ES5 version): call Object.preventExtensions, make sure we can still modify atttibutes on formals without changing the value",
body: function () {
tests.test23Helper(true, "ES5 version");
}
},
test24Helper: function test24Helper(isConvertNeeded, messagePrefix) {
function mkerr(message) {
return messagePrefix + ": " + message;
}
var passedValue = 1;
function f(a, b) {
if (isConvertNeeded) {
Object.defineProperty(arguments, 1, { enumerable: true }); // Force convert to ES5 version.
}
Object.seal(arguments); // This causes configurable = false for all properties + Object.preventExtensions.
assert.areEqual(true, Object.isSealed(arguments), mkerr("isSealed"));
assert.areEqual(false, Object.isExtensible(arguments), mkerr("isExtensible"));
var actual = Object.getOwnPropertyDescriptor(arguments, 0);
var expected = { configurable: false, enumerable: true, writable: true, value: passedValue };
assert.areEqual(expected, actual, mkerr("wrong descriptor"));
}
f(passedValue, passedValue + 1);
},
// Object.freeze(arguments -- not ES5 version) does not set configurable to false on formals.
test24_1: {
name: "arguments (non-ES5 version): call Object.seal, verify descriptor on formal",
body: function () {
tests.test24Helper(false, "non-ES5 version");
}
},
test24_2: {
name: "arguments (ES5 version): call Object.seal, verify descriptor on formal",
body: function () {
tests.test24Helper(true, "ES5 version");
}
},
test25Helper: function test25Helper(isConvertNeeded, messagePrefix) {
function mkerr(message) {
return messagePrefix + ": " + message;
}
var passedValue = 1;
function f(a, b) {
if (isConvertNeeded) {
Object.defineProperty(arguments, 1, { enumerable: true }); // Force convert to ES5 version.
}
Object.freeze(arguments); // This causes configurable AND writable = false for all properties + Object.preventExtensions.
assert.areEqual(true, Object.isFrozen(arguments), mkerr("isFrozen"));
assert.areEqual(true, Object.isSealed(arguments), mkerr("isSealed"));
assert.areEqual(false, Object.isExtensible(arguments), mkerr("isExtensible"));
var actual = Object.getOwnPropertyDescriptor(arguments, 0);
var expected = { configurable: false, enumerable: true, writable: false, value: passedValue };
assert.areEqual(expected, actual, mkerr("wrong descriptor"));
}
f(passedValue, passedValue + 1);
},
// Object.freeze(arguments -- not ES5 version) does not set configurable and writable to false on formals.
test25_1: {
name: "arguments (non-ES5 version): call Object.freeze, verify descriptor on formal",
body: function () {
tests.test25Helper(false, "non-ES5 version");
}
},
test25_2: {
name: "arguments (ES5 version): call Object.freeze, verify descriptor on formal",
body: function () {
tests.test25Helper(true, "ES5 version");
}
},
test26: {
name: "formal arg: delete, preventExtensions, enumerate, make sure the item is deleted",
body: function () {
var passedValue1 = 1;
var passedValue2 = 2;
function f(a, b) {
delete arguments[1];
Object.preventExtensions(arguments);
var accumulator = "";
for (var i in arguments) {
accumulator += i.toString() + ": " + arguments[i] + ";";
}
assert.areEqual("0: " + passedValue1 + ";", accumulator, "accumulator");
assert.areEqual(undefined, arguments[1], "arguments[1]");
}
f(passedValue1, passedValue2);
}
},
test27: {
name: "formal arg: convert to ES5 version, change value and set writable to false",
body: function () {
var passedValue1 = 1;
var val = 2;
function f(a) {
Object.defineProperty(arguments, 0, { enumerable: true });
a = val;
Object.defineProperty(arguments, 0, { writable: false });
var expected = { configurable: true, enumerable: true, writable: false, value: val };
assert.areEqual(expected, Object.getOwnPropertyDescriptor(arguments, 0));
}
f(passedValue1);
}
},
test28: {
name: "formal arg: convert to ES5 version, enumerate when number of actual params is less than number of formals",
body: function () {
var accumulator = "";
function f(a, b) {
Object.preventExtensions(arguments);
for (var i in arguments) {
if (accumulator.length != 0) accumulator += ",";
accumulator += arguments[i];
}
}
var value = 5;
f(value);
var expected = helpers.isVersion10OrLater ?
value.toString() :
value.toString() + ",undefined"; // IE9 compat mode -- Win8 558490.
assert.areEqual(expected, accumulator, "Wrong accumulated value");
}
},
} // tests.
testRunner.runTests(tests);