summaryrefslogtreecommitdiffstats
path: root/js/src/tests/non262/Reflect/set.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/tests/non262/Reflect/set.js')
-rw-r--r--js/src/tests/non262/Reflect/set.js280
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);