Bug 1054426 - Make Object.assign skip null/undefined source arguments if any are passed, to match not-yet-drafted semantics agreed upon in TC39 meeting minutes. Also fixes Facebook bustage. r=mjrosenb over IRC
authorJeff Walden <jwalden@mit.edu>
Tue, 19 Aug 2014 22:10:20 -0700
changeset 200505 a203ef1e2b8ffefa5d512446e83a73e45a0146c0
parent 200504 d209e250a42eaa4229f4bb87362e93fee3d6f0c3
child 200506 789d20c54b14ae2a56580be0c4273c9ff0c5b4f0
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmjrosenb
bugs1054426
milestone34.0a1
Bug 1054426 - Make Object.assign skip null/undefined source arguments if any are passed, to match not-yet-drafted semantics agreed upon in TC39 meeting minutes. Also fixes Facebook bustage. r=mjrosenb over IRC
js/src/builtin/Object.js
js/src/tests/ecma_6/Object/assign.js
--- a/js/src/builtin/Object.js
+++ b/js/src/builtin/Object.js
@@ -9,18 +9,24 @@ function ObjectStaticAssign(target, firs
 
     // Step 3.
     if (arguments.length < 2)
         return to;
 
     // Step 4.
     var i = 1;
     do {
-        // Step 5.a-b.
+        // Step 5.a-b, plus an unspecified flourish to skip null/undefined, so
+        // any site depending on agreed-upon (but not-yet-drafted) semantics
+        // from TC39 meeting minutes will work. (Yes, implausibly, such a site
+        // exists. See bug 1054426.)
         var nextSource = arguments[i];
+        if (nextSource === null || nextSource === undefined)
+            continue;
+
         var from = ToObject(nextSource);
 
         // Step 5.c-d.
         var keysArray = OwnPropertyKeys(from);
 
         // Steps 5.e-f.
         var len = keysArray.length;
 
@@ -53,15 +59,14 @@ function ObjectStaticAssign(target, firs
 
             // Step 5.j.vi.
             nextIndex++;
         }
 
         // Step 5.k.
         if (pendingException !== MISSING)
             throw pendingException;
-        i++;
-    } while (i < arguments.length);
+    } while (++i < arguments.length);
 
     // Step 6.
     return to;
 }
 
--- a/js/src/tests/ecma_6/Object/assign.js
+++ b/js/src/tests/ecma_6/Object/assign.js
@@ -33,22 +33,37 @@ function basicSymbols() {
     var aSymbol = Symbol("aSymbol");
     b[aSymbol] = 22;
     Object.assign(a, b);
     assertEq(a.bProp, 7);
     assertEq(a[aSymbol], 22);
 }
 basicSymbols();
 
-// Calls ToObject() for target and source
+// 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);
-    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 objectEmulatingUndefined === "function") {
+        var falsyObject = objectEmulatingUndefined();
+        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();