[ES6] Implement Reflect.ownKeys
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 26 Jul 2015 21:02:23 +0000 (21:02 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 26 Jul 2015 21:02:23 +0000 (21:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=147307

Reviewed by Sam Weinig.

This patch implements Reflect.ownKeys.
In this patch, we refactor the existing code to list up own keys in the object.
Such code is used by Object.getOwnPropertyNames, Object.getOwnPropertyKeys, Object.keys and @ownEnumerableKeys.
We factor out the listing up own keys as ownPropertyKeys function and also use it in Reflect.ownKeys.

* runtime/ObjectConstructor.cpp:
(JSC::objectConstructorGetOwnPropertyNames):
(JSC::objectConstructorGetOwnPropertySymbols):
(JSC::objectConstructorKeys):
(JSC::ownEnumerablePropertyKeys):
(JSC::ownPropertyKeys):
* runtime/ObjectConstructor.h:
* runtime/ReflectObject.cpp:
(JSC::reflectObjectOwnKeys):
* tests/stress/reflect-own-keys.js: Added.
(shouldBe):
(shouldThrow):
(shouldBeArray):

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/ObjectConstructor.cpp
Source/JavaScriptCore/runtime/ObjectConstructor.h
Source/JavaScriptCore/runtime/ReflectObject.cpp
Source/JavaScriptCore/tests/stress/reflect-own-keys.js [new file with mode: 0644]

index 8365bca..ff5a3b6 100644 (file)
@@ -1,3 +1,29 @@
+2015-07-25  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Implement Reflect.ownKeys
+        https://bugs.webkit.org/show_bug.cgi?id=147307
+
+        Reviewed by Sam Weinig.
+
+        This patch implements Reflect.ownKeys.
+        In this patch, we refactor the existing code to list up own keys in the object.
+        Such code is used by Object.getOwnPropertyNames, Object.getOwnPropertyKeys, Object.keys and @ownEnumerableKeys.
+        We factor out the listing up own keys as ownPropertyKeys function and also use it in Reflect.ownKeys.
+
+        * runtime/ObjectConstructor.cpp:
+        (JSC::objectConstructorGetOwnPropertyNames):
+        (JSC::objectConstructorGetOwnPropertySymbols):
+        (JSC::objectConstructorKeys):
+        (JSC::ownEnumerablePropertyKeys):
+        (JSC::ownPropertyKeys):
+        * runtime/ObjectConstructor.h:
+        * runtime/ReflectObject.cpp:
+        (JSC::reflectObjectOwnKeys):
+        * tests/stress/reflect-own-keys.js: Added.
+        (shouldBe):
+        (shouldThrow):
+        (shouldBeArray):
+
 2015-07-26  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [ES6] Implement Reflect.apply
 2015-07-26  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [ES6] Implement Reflect.apply
index d53f652..377645d 100644 (file)
@@ -251,13 +251,7 @@ EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exe
     JSObject* object = exec->argument(0).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsNull());
     JSObject* object = exec->argument(0).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsNull());
-    PropertyNameArray properties(exec, PropertyNameMode::Strings);
-    object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include));
-    JSArray* names = constructEmptyArray(exec, 0);
-    size_t numProperties = properties.size();
-    for (size_t i = 0; i < numProperties; i++)
-        names->push(exec, jsOwnedString(exec, properties[i].string()));
-    return JSValue::encode(names);
+    return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Include));
 }
 
 // FIXME: Use the enumeration cache.
 }
 
 // FIXME: Use the enumeration cache.
@@ -266,16 +260,7 @@ EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertySymbols(ExecState* e
     JSObject* object = exec->argument(0).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsNull());
     JSObject* object = exec->argument(0).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsNull());
-    PropertyNameArray properties(exec, PropertyNameMode::Symbols);
-    object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include));
-    JSArray* names = constructEmptyArray(exec, 0);
-    size_t numProperties = properties.size();
-    for (size_t i = 0; i < numProperties; i++) {
-        auto impl = properties[i].impl();
-        if (impl->isSymbol() && !exec->propertyNames().isPrivateName(*impl))
-            names->push(exec, Symbol::create(exec->vm(), static_cast<SymbolImpl&>(*impl)));
-    }
-    return JSValue::encode(names);
+    return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Symbols, DontEnumPropertiesMode::Include));
 }
 
 // FIXME: Use the enumeration cache.
 }
 
 // FIXME: Use the enumeration cache.
@@ -284,13 +269,7 @@ EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec)
     JSObject* object = exec->argument(0).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsNull());
     JSObject* object = exec->argument(0).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsNull());
-    PropertyNameArray properties(exec, PropertyNameMode::Strings);
-    object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, properties, EnumerationMode());
-    JSArray* keys = constructEmptyArray(exec, 0);
-    size_t numProperties = properties.size();
-    for (size_t i = 0; i < numProperties; i++)
-        keys->push(exec, jsOwnedString(exec, properties[i].string()));
-    return JSValue::encode(keys);
+    return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude));
 }
 
 EncodedJSValue JSC_HOST_CALL ownEnumerablePropertyKeys(ExecState* exec)
 }
 
 EncodedJSValue JSC_HOST_CALL ownEnumerablePropertyKeys(ExecState* exec)
@@ -298,26 +277,7 @@ EncodedJSValue JSC_HOST_CALL ownEnumerablePropertyKeys(ExecState* exec)
     JSObject* object = exec->argument(0).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsNull());
     JSObject* object = exec->argument(0).toObject(exec);
     if (exec->hadException())
         return JSValue::encode(jsNull());
-    PropertyNameArray properties(exec, PropertyNameMode::Both);
-    object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, properties, EnumerationMode());
-
-    JSArray* keys = constructEmptyArray(exec, 0);
-    Vector<Identifier, 16> propertySymbols;
-    size_t numProperties = properties.size();
-    for (size_t i = 0; i < numProperties; i++) {
-        const auto& identifier = properties[i];
-        if (identifier.isSymbol()) {
-            if (!exec->propertyNames().isPrivateName(identifier))
-                propertySymbols.append(identifier);
-        } else
-            keys->push(exec, jsOwnedString(exec, identifier.string()));
-    }
-
-    // To ensure the order defined in the spec (9.1.12), we append symbols at the last elements of keys.
-    for (const auto& identifier : propertySymbols)
-        keys->push(exec, Symbol::create(exec->vm(), static_cast<SymbolImpl&>(*identifier.impl())));
-
-    return JSValue::encode(keys);
+    return JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Both, DontEnumPropertiesMode::Exclude));
 }
 
 // ES5 8.10.5 ToPropertyDescriptor
 }
 
 // ES5 8.10.5 ToPropertyDescriptor
@@ -654,4 +614,56 @@ EncodedJSValue JSC_HOST_CALL objectConstructorIs(ExecState* exec)
     return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1))));
 }
 
     return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1))));
 }
 
+// FIXME: Use the enumeration cache.
+JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode)
+{
+    PropertyNameArray properties(exec, propertyNameMode);
+    object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode));
+
+    JSArray* keys = constructEmptyArray(exec, 0);
+
+    switch (propertyNameMode) {
+    case PropertyNameMode::Strings: {
+        size_t numProperties = properties.size();
+        for (size_t i = 0; i < numProperties; i++) {
+            ASSERT(!identifier.isSymbol());
+            keys->push(exec, jsOwnedString(exec, properties[i].string()));
+        }
+        break;
+    }
+
+    case PropertyNameMode::Symbols: {
+        size_t numProperties = properties.size();
+        for (size_t i = 0; i < numProperties; i++) {
+            const auto& identifier = properties[i];
+            ASSERT(identifier.isSymbol());
+            if (!exec->propertyNames().isPrivateName(identifier))
+                keys->push(exec, Symbol::create(exec->vm(), static_cast<SymbolImpl&>(*identifier.impl())));
+        }
+        break;
+    }
+
+    case PropertyNameMode::Both: {
+        Vector<Identifier, 16> propertySymbols;
+        size_t numProperties = properties.size();
+        for (size_t i = 0; i < numProperties; i++) {
+            const auto& identifier = properties[i];
+            if (identifier.isSymbol()) {
+                if (!exec->propertyNames().isPrivateName(identifier))
+                    propertySymbols.append(identifier);
+            } else
+                keys->push(exec, jsOwnedString(exec, identifier.string()));
+        }
+
+        // To ensure the order defined in the spec (9.1.12), we append symbols at the last elements of keys.
+        for (const auto& identifier : propertySymbols)
+            keys->push(exec, Symbol::create(exec->vm(), static_cast<SymbolImpl&>(*identifier.impl())));
+
+        break;
+    }
+    }
+
+    return keys;
+}
+
 } // namespace JSC
 } // namespace JSC
index 2aa2c9e..79ec619 100644 (file)
@@ -91,6 +91,7 @@ inline JSObject* constructEmptyObject(ExecState* exec)
 }
 
 JSObject* objectConstructorFreeze(ExecState*, JSObject*);
 }
 
 JSObject* objectConstructorFreeze(ExecState*, JSObject*);
+JSArray* ownPropertyKeys(ExecState*, JSObject*, PropertyNameMode, DontEnumPropertiesMode);
 
 } // namespace JSC
 
 
 } // namespace JSC
 
index a64b7c0..86b7761 100644 (file)
 
 #include "JSCInlines.h"
 #include "Lookup.h"
 
 #include "JSCInlines.h"
 #include "Lookup.h"
+#include "ObjectConstructor.h"
+
+namespace JSC {
+
+static EncodedJSValue JSC_HOST_CALL reflectObjectOwnKeys(ExecState*);
+
+}
 
 #include "ReflectObject.lut.h"
 
 
 #include "ReflectObject.lut.h"
 
@@ -41,6 +48,7 @@ const ClassInfo ReflectObject::s_info = { "Reflect", &Base::s_info, &reflectObje
 @begin reflectObjectTable
     apply           reflectObjectApply          DontEnum|Function 3
     deleteProperty  reflectObjectDeleteProperty DontEnum|Function 2
 @begin reflectObjectTable
     apply           reflectObjectApply          DontEnum|Function 3
     deleteProperty  reflectObjectDeleteProperty DontEnum|Function 2
+    ownKeys         reflectObjectOwnKeys        DontEnum|Function 1
 @end
 */
 
 @end
 */
 
@@ -62,4 +70,12 @@ bool ReflectObject::getOwnPropertySlot(JSObject* object, ExecState* exec, Proper
 
 // ------------------------------ Functions --------------------------------
 
 
 // ------------------------------ Functions --------------------------------
 
+EncodedJSValue JSC_HOST_CALL reflectObjectOwnKeys(ExecState* exec)
+{
+    JSValue target = exec->argument(0);
+    if (!target.isObject())
+        return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.ownKeys requires the first argument be an object")));
+    return JSValue::encode(ownPropertyKeys(exec, jsCast<JSObject*>(target), PropertyNameMode::Both, DontEnumPropertiesMode::Include));
+}
+
 } // namespace JSC
 } // namespace JSC
diff --git a/Source/JavaScriptCore/tests/stress/reflect-own-keys.js b/Source/JavaScriptCore/tests/stress/reflect-own-keys.js
new file mode 100644 (file)
index 0000000..0685de3
--- /dev/null
@@ -0,0 +1,46 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function shouldThrow(func, message) {
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        error = e;
+    }
+    if (!error)
+        throw new Error("not thrown.");
+    if (String(error) !== message)
+        throw new Error("bad error: " + String(error));
+}
+
+function shouldBeArray(actual, expected) {
+    shouldBe(actual.length, expected.length);
+    for (var i = 0; i < expected.length; ++i) {
+        try {
+            shouldBe(actual[i], expected[i]);
+        } catch(e) {
+            print(JSON.stringify(actual));
+            throw e;
+        }
+    }
+}
+
+shouldBe(Reflect.ownKeys.length, 1);
+
+shouldThrow(() => {
+    Reflect.ownKeys("hello");
+}, `TypeError: Reflect.ownKeys requires the first argument be an object`);
+
+var cocoa = Symbol("Cocoa");
+var cappuccino = Symbol("Cappuccino");
+
+shouldBeArray(Reflect.ownKeys({}), []);
+shouldBeArray(Reflect.ownKeys({42:42}), ['42']);
+shouldBeArray(Reflect.ownKeys({0:0,1:1,2:2}), ['0','1','2']);
+shouldBeArray(Reflect.ownKeys({0:0,1:1,2:2,hello:42}), ['0','1','2','hello']);
+shouldBeArray(Reflect.ownKeys({hello:42,0:0,1:1,2:2,world:42}), ['0','1','2','hello','world']);
+shouldBeArray(Reflect.ownKeys({[cocoa]:42,hello:42,0:0,1:1,2:2,world:42}), ['0','1','2','hello','world', cocoa]);
+shouldBeArray(Reflect.ownKeys({[cocoa]:42,hello:42,0:0,1:1,2:2,[cappuccino]:42,world:42}), ['0','1','2','hello','world', cocoa, cappuccino]);