[ES2016] Implement Object.entries
authorgskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 12 Aug 2016 20:46:24 +0000 (20:46 +0000)
committergskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 12 Aug 2016 20:46:24 +0000 (20:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=160412

Reviewed by Saam Barati.

This patch adds entries function to Object that returns list of
key+values pairs. Patch did according to the point of
spec https://tc39.github.io/ecma262/#sec-object.entries

Source/JavaScriptCore:

* builtins/ObjectConstructor.js:
(globalPrivate.enumerableOwnProperties):
(entries):
* runtime/ObjectConstructor.cpp:

JSTests:

* stress/object-entries.js:
(compare):
(string_appeared_here.forEach):
(const.getInvokedFunctions.):
(const.getInvokedFunctions):
(Array.prototype.push):
* stress/object-values.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@204419 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChangeLog
JSTests/stress/object-entries.js [new file with mode: 0644]
JSTests/stress/object-values.js
LayoutTests/ChangeLog
LayoutTests/js/Object-getOwnPropertyNames-expected.txt
LayoutTests/js/script-tests/Object-getOwnPropertyNames.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/ObjectConstructor.js
Source/JavaScriptCore/runtime/ObjectConstructor.cpp

index 1f1ecd7..0e782e3 100644 (file)
@@ -1,3 +1,21 @@
+2016-08-12  Skachkov Oleksandr  <gskachkov@gmail.com>
+
+        [ES2016] Implement Object.entries
+        https://bugs.webkit.org/show_bug.cgi?id=160412
+
+        Reviewed by Saam Barati.
+
+        Patch contains tests for Object.entries function and 
+        fix of wrong tests for Object.values function.
+
+        * stress/object-entries.js: 
+        (compare):
+        (string_appeared_here.forEach):
+        (const.getInvokedFunctions.):
+        (const.getInvokedFunctions):
+        (Array.prototype.push):
+        * stress/object-values.js:
+
 2016-08-11  Mark Lam  <mark.lam@apple.com>
 
         OverridesHasInstance should not branch across register allocations.
diff --git a/JSTests/stress/object-entries.js b/JSTests/stress/object-entries.js
new file mode 100644 (file)
index 0000000..04ad5bc
--- /dev/null
@@ -0,0 +1,123 @@
+var obj = Object.create({ a: "qux", d: "qux" });
+obj.a = "foo"; obj.b = "bar"; obj.c = "baz";
+var entries = Object.entries(obj);
+var passed = Array.isArray(entries) 
+    && String(entries[0]) === "a,foo"
+    && String(entries[1]) === "b,bar"
+    && String(entries[2]) === "c,baz";
+
+if (!passed)
+    throw new Error("Object.entries return wrong result.");
+
+var complexObject = {
+    obj : {
+        a: 'x',
+        b: 'y'
+    },
+    primitive : 'z'
+};
+
+passed = false;
+entries = Object.entries(complexObject);
+
+passed = entries.length === 2 
+    && entries[0][0] === 'obj' 
+    && entries[0][1].a === 'x' 
+    && entries[0][1].b === 'y' 
+    && entries[1][0] === 'primitive' 
+    && entries[1][1] === 'z';
+
+if (!passed)
+    throw new Error("Object.entries return wrong result.");
+
+entries = Object.entries({ a: 'abcdef' });
+
+passed = entries.length === 1 
+    && entries[0][0] === 'a'
+    && entries[0][1] === 'abcdef';
+
+if (!passed)
+    throw new Error("Object.entries return wrong result.");
+
+var primitives = [
+    ["string", [[0, 's'], [1, 't'], [2, 'r'], [3, 'i'], [4, 'n'], [5, 'g']]],
+    [42, []],
+    [Symbol("symbol"), []],
+    [true, []],
+    [false, []]
+];
+
+function compare(ax, bx) {
+    if (ax.length !== bx.length)
+        return false;
+    for (var i = 0, iz = ax.length; i < iz; ++i) {
+        if (String(ax[i]) !== String(bx[i]))
+            return false;
+    }
+    return true;
+}
+
+for (var [primitive, expected] of primitives) {
+    var ret = Object.entries(primitive);
+    if (!compare(ret, expected))
+        throw new Error("bad value for " + String(primitive) + ": " + String(ret));
+}
+
+[
+    [ null, "TypeError: Object.entries requires that input parameter not be null or undefined" ],
+    [ undefined, "TypeError: Object.entries requires that input parameter not be null or undefined" ]
+].forEach(function ([value, message]) {
+    var error = null;
+    try {
+        Object.entries(value);
+    } catch (e) {
+        error = e;
+    }
+    if (!error)
+        throw new Error("error not thrown");
+    if (String(error) !== message)
+        throw new Error("bad error: " + String(error));
+});
+
+const getInvokedFunctions = (obj) => {
+    let arr = []
+    let p = new Proxy(obj, {
+        ownKeys: function(...args) {
+            arr.push("ownKeys");
+            return Reflect.ownKeys(...args);
+        },
+        getOwnPropertyDescriptor: function(...args) {
+            arr.push("getOwnPropertyDescriptor");
+            return Reflect.getOwnPropertyDescriptor(...args);
+        }
+    });
+
+    Object.entries(p);
+    return arr;
+};
+
+const arr1 = getInvokedFunctions({});
+passed = arr1.length === 1 && arr1[0] === "ownKeys";
+
+if (!passed)
+    throw new Error("Object.entries should invoke ownkeys.");
+
+const arr2 = getInvokedFunctions({a:'foo', b:'boo'});
+passed = arr2.length === 3 && arr2[0] === "ownKeys";
+
+if (!passed)
+    throw new Error("Object.entries should invoke ownkeys.");
+
+passed = arr2[1] === "getOwnPropertyDescriptor";
+
+if (!passed)
+    throw new Error("Object.entries should get property descriptor.");
+
+Array.prototype.push = function () { throw new Error("Array.prototype.push should not be used during invoking of Object.entries.")};
+Array.prototype.getOwnPropertyDescriptor = function () { throw new Error("Array.prototype.getOwnPropertyDescriptor should not be used during invoking of Object.entries.")};
+
+entries = Object.entries({a:'1-2', b:'3-4'});
+passed = Array.isArray(entries) && String(entries[0]) === "a,1-2" && String(entries[1]) === "b,3-4";
+
+if (!passed)
+    throw new Error("Object.entries return wrong result.");
index d903e55..c4b53e5 100644 (file)
@@ -17,14 +17,14 @@ var complexObject = {
 passed = false;
 values = Object.values(complexObject);
 
-passed = values.length !== 2 || values[0].a !== 'x' || values[0].a !== 'y' || values[1] !== 'z';
+passed = values.length === 2 && values[0].a === 'x' && values[0].b === 'y' && values[1] === 'z';
 
 if (!passed)
     throw new Error("Object.values return wrong result.");
 
 values = Object.values({ a: 'abcdef' });
 
-passed = values.length === 1 || values[0].a !== 'abcdef';
+passed = values.length === 1 && values[0] === 'abcdef';
 
 if (!passed)
     throw new Error("Object.values return wrong result.");
@@ -93,7 +93,7 @@ if (!passed)
     throw new Error("Object.values should invoke ownkeys.");
 
 const arr2 = getInvokedFunctions({a:'foo', b:'boo'});
-passed = arr2.length > 1 && arr2[0] === "ownKeys";
+passed = arr2.length === 3 && arr2[0] === "ownKeys";
 
 if (!passed)
     throw new Error("Object.values should invoke ownkeys.");
@@ -104,6 +104,7 @@ if (!passed)
     throw new Error("Object.values should get property descriptor.");
 
 Array.prototype.push = function () { throw new Error("Array.prototype.push should not be used during invoking of Object.values.")};
+Array.prototype.getOwnPropertyDescriptor = function () { throw new Error("Array.prototype.getOwnPropertyDescriptor should not be used during invoking of Object.entries.")};
 
 values = Object.values({a:'1-2', b:'3-4'});
 passed = Array.isArray(values) && String(values) === "1-2,3-4";
index 3b87d91..7517e7b 100644 (file)
@@ -1,3 +1,13 @@
+2016-08-12  Skachkov Oleksandr  <gskachkov@gmail.com>
+
+        [ES2016] Implement Object.entries
+        https://bugs.webkit.org/show_bug.cgi?id=160412
+
+        Reviewed by Saam Barati.
+
+        * js/Object-getOwnPropertyNames-expected.txt:
+        * js/script-tests/Object-getOwnPropertyNames.js:
+
 2016-08-12  Pranjal Jumde  <pjumde@apple.com>
 
         ASSERTION FAILED: : line >= firstLine in BytecodeGenerator::emitExpressionInfo.
index 18ef136..ffbd915 100644 (file)
@@ -41,7 +41,7 @@ PASS getSortedOwnPropertyNames(decodeURI) is ['length', 'name']
 PASS getSortedOwnPropertyNames(decodeURIComponent) is ['length', 'name']
 PASS getSortedOwnPropertyNames(encodeURI) is ['length', 'name']
 PASS getSortedOwnPropertyNames(encodeURIComponent) is ['length', 'name']
-PASS getSortedOwnPropertyNames(Object) is ['assign', 'create', 'defineProperties', 'defineProperty', 'freeze', 'getOwnPropertyDescriptor', 'getOwnPropertyDescriptors', 'getOwnPropertyNames', 'getOwnPropertySymbols', 'getPrototypeOf', 'is', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'length', 'name', 'preventExtensions', 'prototype', 'seal', 'setPrototypeOf', 'values']
+PASS getSortedOwnPropertyNames(Object) is ['assign', 'create', 'defineProperties', 'defineProperty', 'entries', 'freeze', 'getOwnPropertyDescriptor', 'getOwnPropertyDescriptors', 'getOwnPropertyNames', 'getOwnPropertySymbols', 'getPrototypeOf', 'is', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'length', 'name', 'preventExtensions', 'prototype', 'seal', 'setPrototypeOf', 'values']
 PASS getSortedOwnPropertyNames(Object.prototype) is ['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', '__proto__', 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']
 PASS getSortedOwnPropertyNames(Function) is ['length', 'name', 'prototype']
 PASS getSortedOwnPropertyNames(Function.prototype) is ['apply', 'bind', 'call', 'constructor', 'length', 'name', 'toString']
index 6123bb6..28dfa52 100644 (file)
@@ -50,7 +50,7 @@ var expectedPropertyNamesSet = {
     "encodeURI": "['length', 'name']",
     "encodeURIComponent": "['length', 'name']",
 // Built-in ECMA objects
-    "Object": "['assign', 'create', 'defineProperties', 'defineProperty', 'freeze', 'getOwnPropertyDescriptor', 'getOwnPropertyDescriptors', 'getOwnPropertyNames', 'getOwnPropertySymbols', 'getPrototypeOf', 'is', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'length', 'name', 'preventExtensions', 'prototype', 'seal', 'setPrototypeOf', 'values']",
+    "Object": "['assign', 'create', 'defineProperties', 'defineProperty', 'entries', 'freeze', 'getOwnPropertyDescriptor', 'getOwnPropertyDescriptors', 'getOwnPropertyNames', 'getOwnPropertySymbols', 'getPrototypeOf', 'is', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'length', 'name', 'preventExtensions', 'prototype', 'seal', 'setPrototypeOf', 'values']",
     "Object.prototype": "['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', '__proto__', 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']",
     "Function": "['length', 'name', 'prototype']",
     "Function.prototype": "['apply', 'bind', 'call', 'constructor', 'length', 'name', 'toString']",
index 5c38816..5224762 100644 (file)
@@ -1,3 +1,19 @@
+2016-08-12  Skachkov Oleksandr  <gskachkov@gmail.com>
+
+        [ES2016] Implement Object.entries
+        https://bugs.webkit.org/show_bug.cgi?id=160412
+
+        Reviewed by Saam Barati.
+
+        This patch adds entries function to Object that returns list of 
+        key+values pairs. Patch did according to the point of
+        spec https://tc39.github.io/ecma262/#sec-object.entries
+
+        * builtins/ObjectConstructor.js:
+        (globalPrivate.enumerableOwnProperties):
+        (entries):
+        * runtime/ObjectConstructor.cpp:
+
 2016-08-11  Mark Lam  <mark.lam@apple.com>
 
         OverridesHasInstance should not branch across register allocations.
index 1718d82..d53744e 100644 (file)
@@ -39,7 +39,8 @@ function enumerableOwnProperties(object, kind)
             if (descriptor !== @undefined && descriptor.enumerable) {
                 if (kind === @iterationKindValue)
                     properties.@push(obj[nextKey]);
-            // FIXME: Implement 'key+value' and 'key' cases
+                else if (kind === @iterationKindKeyValue)
+                    properties.@push([nextKey, obj[nextKey]]);
             }
         }
     }
@@ -57,6 +58,16 @@ function values(object)
     return @enumerableOwnProperties(object, @iterationKindValue);
 }
 
+function entries(object)
+{
+    "use strict";
+    
+    if (object == null)
+        throw new @TypeError("Object.entries requires that input parameter not be null or undefined");
+    
+    return @enumerableOwnProperties(object, @iterationKindKeyValue);
+}
+
 function assign(target/*[*/, /*...*/sources/*] */)
 {
     "use strict";
index ddaaf3e..46d0cc8 100644 (file)
@@ -85,6 +85,7 @@ const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_i
   is                        objectConstructorIs                         DontEnum|Function 2
   assign                    JSBuiltin                                   DontEnum|Function 2
   values                    JSBuiltin                                   DontEnum|Function 1
+  entries                   JSBuiltin                                   DontEnum|Function 1
 @end
 */