diff options
Diffstat (limited to 'js/src/tests/non262/Reflect/set.js')
-rw-r--r-- | js/src/tests/non262/Reflect/set.js | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/js/src/tests/non262/Reflect/set.js b/js/src/tests/non262/Reflect/set.js new file mode 100644 index 0000000000..83cd98b691 --- /dev/null +++ b/js/src/tests/non262/Reflect/set.js @@ -0,0 +1,280 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ */ + +// Reflect.set does property assignment. +// With three arguments, this is pretty straightforward. +var obj = {}; +assertEq(Reflect.set(obj, "prop", "value"), true); +assertEq(obj.prop, "value"); + + +// === Various targets + +// It can assign array elements. +var arr = ["duck", "duck", "duck"]; +assertEq(Reflect.set(arr, 2, "goose"), true); +assertEq(arr[2], "goose"); + +// It can extend an array. +assertEq(Reflect.set(arr, 3, "Model T"), true); +assertEq(arr.length, 4); + +// It can truncate an array. +assertEq(Reflect.set(arr, "length", 1), true); +assertDeepEq(arr, ["duck"]); + +// It won't assign to non-writable properties of String objects. +var str = new String("hello"); +assertEq(Reflect.set(str, "0", "y"), false); +assertEq(str[0], "h"); +assertEq(Reflect.set(str, "length", 700), false); +assertEq(str.length, 5); + + +// === Receivers +// The optional fourth argument is the receiver, which [[Set]] methods use for +// various things. + +// On ordinary objects, if the property has a setter, the receiver is passed as +// the this-value to the setter. +var expected; +var obj = { + set prop(v) { + "use strict"; + assertEq(v, 32); + assertEq(this, expected); + } +}; +for (expected of [obj, {}, [], 37.3]) { + assertEq(Reflect.set(obj, "prop", 32, expected), true); +} + +// If the property doesn't already exist, it is defined on the receiver. +obj = {}; +var obj2 = {}; +assertEq(Reflect.set(obj, "prop", 47, obj2), true); +assertDeepEq(obj, {}); +assertDeepEq(Reflect.getOwnPropertyDescriptor(obj2, "prop"), + {value: 47, writable: true, enumerable: true, configurable: true}); + +// If the property doesn't already exist, and the receiver isn't an object, return false. +for (var v of SOME_PRIMITIVE_VALUES) { + assertEq(Reflect.set({}, "x", 0, v), false); +} + +// Receiver defaults to the target. +obj = {}; +var hits; +var expectedReceiver; +var proxy = new Proxy(obj, { + set(t, k, v, r) { + assertEq(t, obj); + assertEq(k, "key"); + assertEq(v, "value"); + assertEq(r, expectedReceiver); // not obj + hits++; + return true; + } +}); +hits = 0; +expectedReceiver = proxy; +assertEq(Reflect.set(proxy, "key", "value"), true); +assertEq(hits, 1); + +// But not if explicitly present and undefined. +hits = 0; +expectedReceiver = undefined; +assertEq(Reflect.set(proxy, "key", "value", undefined), true); +assertEq(hits, 1); + +// Reflect.set can be used as fallback behavior in a proxy handler .set() +// method. +var log; +obj = { + set prop(v) { + log += "p"; + assertEq(v, "value"); + assertEq(this, proxy); // not obj! + } +}; +proxy = new Proxy(obj, { + set(t, k, v, r) { + assertEq(t, obj); + assertEq(r, proxy); + log += "s"; + return Reflect.set(t, k, v, r); + } +}); +log = ""; +assertEq(Reflect.set(proxy, "prop", "value"), true); +assertEq(log, "sp"); + + +// === Cross-compartment wrapper behavior. + +// When calling a cross-compartment wrapper, receiver is rewrapped for the +// target compartment. +var g = newGlobal(); +if (!("assertEq" in g)) + g.assertEq = assertEq; // necessary in the browser +g.eval(` + var hits; + var obj = { + set x(v) { + "use strict"; + assertEq(this, receiver); + assertEq(v, "xyzzy"); + hits++; + } + }; + var receiver = {}; +`); +g.hits = 0; +assertEq(Reflect.set(g.obj, "x", "xyzzy", g.receiver), true); +assertEq(g.hits, 1); + +// ...even when receiver is from a different compartment than target. +var receiver = {}; +g.receiver = receiver; +g.hits = 0; +assertEq(Reflect.set(g.obj, "x", "xyzzy", receiver), true); +assertEq(g.hits, 1); + +// ...even when receiver is a primtive value, even undefined. +for (receiver of SOME_PRIMITIVE_VALUES) { + g.receiver = receiver; + g.hits = 0; + assertEq(Reflect.set(g.obj, "x", "xyzzy", receiver), true); + assertEq(g.hits, 1); +} + + +// === Less than 3 arguments + +// With two arguments, the value is assumed to be undefined. +obj = {}; +assertEq(Reflect.set(obj, "size"), true); +assertDeepEq(Reflect.getOwnPropertyDescriptor(obj, "size"), + {value: undefined, writable: true, enumerable: true, configurable: true}); + +// With just one argument, the key is "undefined". +obj = {}; +assertEq(Reflect.set(obj), true); +assertDeepEq(Reflect.getOwnPropertyDescriptor(obj, "undefined"), + {value: undefined, writable: true, enumerable: true, configurable: true}); + +// For the no argument-case, see target.js. + + +// === Failure cases + +// Non-writable data property +obj = {}; +Reflect.defineProperty(obj, "x", {value: 0, writable: false}); +assertEq(Reflect.set(obj, "x", 1), false); +assertEq(obj.x, 0); + +// The same, but inherited from a prototype +var obj2 = Object.create(obj); +assertEq(Reflect.set(obj2, "x", 1), false); +assertEq(obj2.hasOwnProperty("x"), false); +assertEq(obj2.x, 0); + +// Getter, no setter +obj = {}; +var desc = {get: () => 12, set: undefined, enumerable: false, configurable: true}; +Reflect.defineProperty(obj, "y", desc); +assertEq(Reflect.set(obj, "y", 13), false); +assertDeepEq(Reflect.getOwnPropertyDescriptor(obj, "y"), desc); + +// The same, but inherited from a prototype +obj2 = Object.create(obj); +assertEq(Reflect.set(obj2, "y", 1), false); +assertEq(obj2.hasOwnProperty("y"), false); +assertDeepEq(Reflect.getOwnPropertyDescriptor(obj, "y"), desc); + +// Proxy set handler returns a false value +for (var no of [false, ""]) { + var hits = 0; + obj = {}; + var proxy = new Proxy(obj, { + set(t, k, v, r) { + assertEq(t, obj); + assertEq(k, "x"); + assertEq(v, 33); + assertEq(r, proxy); + hits++; + return no; + } + }); + assertEq(Reflect.set(proxy, "x", 33), false); + assertEq(hits, 1); + assertEq("x" in obj, false); +} + +// Proxy handler method throws +obj = {}; +proxy = new Proxy(obj, { + set(t, k, v, r) { throw "i don't like " + v; } +}); +assertThrowsValue(() => Reflect.set(proxy, "food", "cheese"), "i don't like cheese"); + +// If a Proxy set handler breaks the object invariants, it's a TypeError. +for (obj of [{a: 0}, {get a() { return 0; }}]) { + Object.freeze(obj); + proxy = new Proxy(obj, { + set(t, k, v, r) { return true; } + }); + assertThrowsInstanceOf(() => Reflect.set(proxy, "a", "b"), TypeError); +} + +// Per spec, this should first call p.[[Set]]("0", 42, a) and +// then (since p has no own properties) a.[[Set]]("0", 42, a). +// The latter should not define a property on p. +var a = [0, 1, 2, 3]; +var p = Object.create(a); +Reflect.set(p, "0", 42, a); +assertEq(p.hasOwnProperty("0"), false); +assertDeepEq(Reflect.getOwnPropertyDescriptor(a, "0"), + {value: 42, writable: true, enumerable: true, configurable: true}); + +// Test behavior of ordinary objects' [[Set]] method (ES6 9.1.9). +// On an ordinary object, if the property key isn't present, [[Set]] calls +// receiver.[[GetOwnProperty]]() and then receiver.[[DefineProperty]](). +var log; +obj = {}; +var proxyTarget = {}; +var existingDescriptor, expected, defineResult; +var receiver = new Proxy(proxyTarget, { + getOwnPropertyDescriptor(t, k) { + log += "g"; + return existingDescriptor; + }, + defineProperty(t, k, desc) { + log += "d"; + assertEq(t, proxyTarget); + assertEq(k, "prop"); + assertDeepEq(desc, expected); + return defineResult; + } +}); +existingDescriptor = undefined; +expected = {value: 5, writable: true, enumerable: true, configurable: true}; +for (var defineResult of [true, false]) { + log = ""; + assertEq(Reflect.set(obj, "prop", 5, receiver), defineResult); + assertEq(log, "gd"); +} + +existingDescriptor = {value: 7, writable: true, enumerable: false, configurable: true}; +expected = {value: 4}; +for (var defineResult of [true, false]) { + log = ""; + assertEq(Reflect.set(obj, "prop", 4, receiver), defineResult); + assertEq(log, "gd"); +} + + +// For more Reflect.set tests, see target.js and propertyKeys.js. + +reportCompare(0, 0); |