summaryrefslogtreecommitdiffstats
path: root/js/src/tests/non262/object/assign.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/tests/non262/object/assign.js303
1 files changed, 303 insertions, 0 deletions
diff --git a/js/src/tests/non262/object/assign.js b/js/src/tests/non262/object/assign.js
new file mode 100644
index 0000000000..8828bdb21c
--- /dev/null
+++ b/js/src/tests/non262/object/assign.js
@@ -0,0 +1,303 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function checkDataProperty(object, propertyKey, value, writable, enumerable, configurable) {
+ var desc = Object.getOwnPropertyDescriptor(object, propertyKey);
+ assertEq(desc === undefined, false);
+ assertEq('value' in desc, true);
+ assertEq(desc.value, value);
+ assertEq(desc.writable, writable);
+ assertEq(desc.enumerable, enumerable);
+ assertEq(desc.configurable, configurable);
+}
+
+/* 19.1.2.1 Object.assign ( target, ...sources ) */
+assertEq(Object.assign.length, 2);
+
+// Basic functionality works with multiple sources
+function basicMultipleSources() {
+ var a = {};
+ var b = { bProp : 7 };
+ var c = { cProp : 8 };
+ Object.assign(a, b, c);
+ assertEq(a.bProp, 7);
+ assertEq(a.cProp, 8);
+}
+basicMultipleSources();
+
+// Basic functionality works with symbols (Bug 1052358)
+function basicSymbols() {
+ var a = {};
+ var b = { bProp : 7 };
+ var aSymbol = Symbol("aSymbol");
+ b[aSymbol] = 22;
+ Object.assign(a, b);
+ assertEq(a.bProp, 7);
+ assertEq(a[aSymbol], 22);
+}
+basicSymbols();
+
+// Calls ToObject() for target, skips null/undefined sources, uses
+// ToObject(source) otherwise.
+function testToObject() {
+ assertThrowsInstanceOf(() => Object.assign(null, null), TypeError);
+ assertThrowsInstanceOf(() => Object.assign(), TypeError);
+ assertThrowsInstanceOf(() => Object.assign(null, {}), TypeError);
+ assertEq(Object.assign({}, null) instanceof Object, true);
+ assertEq(Object.assign({}, undefined) instanceof Object, true);
+
+ // Technically an embedding could have this as extension acting differently
+ // from ours, so a feature-test is inadequate. We can move this subtest
+ // into extensions/ if that ever matters.
+ if (typeof createIsHTMLDDA === "function") {
+ var falsyObject = createIsHTMLDDA();
+ falsyObject.foo = 7;
+
+ var obj = Object.assign({}, falsyObject);
+ assertEq(obj instanceof Object, true);
+ assertEq(obj.foo, 7);
+ }
+
+ assertEq(Object.assign(true, {}) instanceof Boolean, true);
+ assertEq(Object.assign(1, {}) instanceof Number, true);
+ assertEq(Object.assign("string", {}) instanceof String, true);
+ var o = {};
+ assertEq(Object.assign(o, {}), o);
+}
+testToObject();
+
+// Invokes [[OwnPropertyKeys]] on ToObject(source)
+function testOwnPropertyKeys() {
+ assertThrowsInstanceOf(() => Object.assign(null, new Proxy({}, {
+ getOwnPropertyNames: () => { throw new Error("not called"); }
+ })), TypeError);
+
+ var ownKeysCalled = false;
+ Object.assign({}, new Proxy({}, {
+ ownKeys: function() {
+ ownKeysCalled = true;
+ return [];
+ }
+ }));
+ assertEq(ownKeysCalled, true);
+};
+testOwnPropertyKeys();
+
+// Ensure correct property traversal
+function correctPropertyTraversal() {
+ var log = "";
+ var source = new Proxy({a: 1, b: 2}, {
+ ownKeys: () => ["b", "c", "a"],
+ getOwnPropertyDescriptor: function(t, pk) {
+ log += "#" + pk;
+ return Object.getOwnPropertyDescriptor(t, pk);
+ },
+ get: function(t, pk, r) {
+ log += "-" + pk;
+ return t[pk];
+ },
+ });
+ Object.assign({}, source);
+ assertEq(log, "#b-b#c#a-a");
+}
+correctPropertyTraversal();
+
+// Only [[Enumerable]] properties are assigned to target
+function onlyEnumerablePropertiesAssigned() {
+ var source = Object.defineProperties({}, {
+ a: {value: 1, enumerable: true},
+ b: {value: 2, enumerable: false},
+ });
+ var target = Object.assign({}, source);
+ assertEq("a" in target, true);
+ assertEq("b" in target, false);
+}
+onlyEnumerablePropertiesAssigned();
+
+
+// Enumerability is decided on-time, not before main loop (1)
+function testEnumerabilityDeterminedInLoop1()
+{
+ var getterCalled = false;
+ var sourceTarget = {
+ get a() { getterCalled = true },
+ get b() { Object.defineProperty(sourceTarget, "a", {enumerable: false}) },
+ };
+ var source = new Proxy(sourceTarget, { ownKeys: () => ["b", "a"] });
+ Object.assign({}, source);
+ assertEq(getterCalled, false);
+}
+testEnumerabilityDeterminedInLoop1();
+
+// Enumerability is decided on-time, not before main loop (2)
+function testEnumerabilityDeterminedInLoop2()
+{
+ var getterCalled = false;
+ var sourceTarget = {
+ get a() { getterCalled = true },
+ get b() { Object.defineProperty(sourceTarget, "a", {enumerable: true}) },
+ };
+ var source = new Proxy(sourceTarget, {
+ ownKeys: () => ["b", "a"]
+ });
+ Object.defineProperty(sourceTarget, "a", {enumerable: false});
+ Object.assign({}, source);
+ assertEq(getterCalled, true);
+}
+testEnumerabilityDeterminedInLoop2();
+
+// Properties are retrieved through Get() and assigned onto
+// the target as data properties, not in any sense cloned over as descriptors
+function testPropertiesRetrievedThroughGet() {
+ var getterCalled = false;
+ Object.assign({}, {get a() { getterCalled = true }});
+ assertEq(getterCalled, true);
+}
+testPropertiesRetrievedThroughGet();
+
+// Properties are retrieved through Get()
+// Properties are assigned through Put()
+function testPropertiesAssignedThroughPut() {
+ var setterCalled = false;
+ Object.assign({set a(v) { setterCalled = v }}, {a: true});
+ assertEq(setterCalled, true);
+}
+testPropertiesAssignedThroughPut();
+
+// Properties are retrieved through Get()
+// Properties are assigned through Put(): Existing property attributes are not altered
+function propertiesAssignedExistingNotAltered() {
+ var source = {a: 1, b: 2, c: 3};
+ var target = {a: 0, b: 0, c: 0};
+ Object.defineProperty(target, "a", {enumerable: false});
+ Object.defineProperty(target, "b", {configurable: false});
+ Object.defineProperty(target, "c", {enumerable: false, configurable: false});
+ Object.assign(target, source);
+ checkDataProperty(target, "a", 1, true, false, true);
+ checkDataProperty(target, "b", 2, true, true, false);
+ checkDataProperty(target, "c", 3, true, false, false);
+}
+propertiesAssignedExistingNotAltered();
+
+// Properties are retrieved through Get()
+// Properties are assigned through Put(): Throws TypeError if non-writable
+function propertiesAssignedTypeErrorNonWritable() {
+ var source = {a: 1};
+ var target = {a: 0};
+ Object.defineProperty(target, "a", {writable: false});
+ assertThrowsInstanceOf(() => Object.assign(target, source), TypeError);
+ checkDataProperty(target, "a", 0, false, true, true);
+}
+propertiesAssignedTypeErrorNonWritable();
+
+// Properties are retrieved through Get()
+// Put() creates standard properties; Property attributes from source are ignored
+function createsStandardProperties() {
+ var source = {a: 1, b: 2, c: 3, get d() { return 4 }};
+ Object.defineProperty(source, "b", {writable: false});
+ Object.defineProperty(source, "c", {configurable: false});
+ var target = Object.assign({}, source);
+ checkDataProperty(target, "a", 1, true, true, true);
+ checkDataProperty(target, "b", 2, true, true, true);
+ checkDataProperty(target, "c", 3, true, true, true);
+ checkDataProperty(target, "d", 4, true, true, true);
+}
+createsStandardProperties();
+
+// Properties created during traversal are not copied
+function propertiesCreatedDuringTraversalNotCopied() {
+ var source = {get a() { this.b = 2 }};
+ var target = Object.assign({}, source);
+ assertEq("a" in target, true);
+ assertEq("b" in target, false);
+}
+propertiesCreatedDuringTraversalNotCopied();
+
+// Properties deleted during traversal are not copied
+function testDeletePropertiesNotCopied() {
+ var source = new Proxy({
+ get a() { delete this.b },
+ b: 2,
+ }, {
+ getOwnPropertyNames: () => ["a", "b"]
+ });
+ var target = Object.assign({}, source);
+ assertEq("a" in target, true);
+ assertEq("b" in target, false);
+}
+testDeletePropertiesNotCopied();
+
+function testDeletionExposingShadowedProperty()
+{
+ var srcProto = { b: 42 };
+ var src =
+ Object.create(srcProto,
+ { a: { enumerable: true, get: function() { delete this.b; } },
+ b: { value: 2, configurable: true, enumerable: true } });
+ var source = new Proxy(src, { getOwnPropertyNames: () => ["a", "b"] });
+ var target = Object.assign({}, source);
+ assertEq("a" in target, true);
+ assertEq("b" in target, false);
+}
+testDeletionExposingShadowedProperty();
+
+// Properties first deleted and then recreated during traversal are copied (1)
+function testDeletedAndRecreatedPropertiesCopied1() {
+ var source = new Proxy({
+ get a() { delete this.c },
+ get b() { this.c = 4 },
+ c: 3,
+ }, {
+ getOwnPropertyNames: () => ["a", "b", "c"]
+ });
+ var target = Object.assign({}, source);
+ assertEq("a" in target, true);
+ assertEq("b" in target, true);
+ assertEq("c" in target, true);
+ checkDataProperty(target, "c", 4, true, true, true);
+}
+testDeletedAndRecreatedPropertiesCopied1();
+
+// Properties first deleted and then recreated during traversal are copied (2)
+function testDeletedAndRecreatedPropertiesCopied2() {
+ var source = new Proxy({
+ get a() { delete this.c },
+ get b() { this.c = 4 },
+ c: 3,
+ }, {
+ ownKeys: () => ["a", "c", "b"]
+ });
+ var target = Object.assign({}, source);
+ assertEq("a" in target, true);
+ assertEq("b" in target, true);
+ assertEq("c" in target, false);
+}
+testDeletedAndRecreatedPropertiesCopied2();
+
+// String and Symbol valued properties are copied
+function testStringAndSymbolPropertiesCopied() {
+ var keyA = "str-prop";
+ var source = {"str-prop": 1};
+ var target = Object.assign({}, source);
+ checkDataProperty(target, keyA, 1, true, true, true);
+}
+testStringAndSymbolPropertiesCopied();
+
+// Intermediate exceptions stop traversal and throw exception
+function testExceptionsDoNotStopFirstReported1() {
+ var TestError = function TestError() {};
+ var source = new Proxy({}, {
+ getOwnPropertyDescriptor: function(t, pk) {
+ assertEq(pk, "b");
+ throw new TestError();
+ },
+ ownKeys: () => ["b", "a"]
+ });
+ assertThrowsInstanceOf(() => Object.assign({}, source), TestError);
+}
+testExceptionsDoNotStopFirstReported1();
+
+
+if (typeof reportCompare == "function")
+ reportCompare(true, true);