[ES6] Implement Reflect.getPrototypeOf and Reflect.setPrototypeOf
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Aug 2015 17:57:08 +0000 (17:57 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Aug 2015 17:57:08 +0000 (17:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=147874

Reviewed by Darin Adler.

This patch implements ES6 Reflect.{getPrototypeOf, setPrototypeOf}.
The difference from the Object.* one is

1. They dont not perform ToObject onto the non-object arguments. They make it as a TypeError.
2. Reflect.setPrototyeOf returns false when the operation is failed. In Object.setPrototypeOf, it raises a TypeError.

* runtime/ObjectConstructor.cpp:
(JSC::ObjectConstructorGetPrototypeOfFunctor::ObjectConstructorGetPrototypeOfFunctor):
(JSC::ObjectConstructorGetPrototypeOfFunctor::result):
(JSC::ObjectConstructorGetPrototypeOfFunctor::operator()):
(JSC::objectConstructorGetPrototypeOf):
* runtime/ObjectConstructor.h:
* runtime/ReflectObject.cpp:
(JSC::reflectObjectGetPrototypeOf):
(JSC::reflectObjectSetPrototypeOf):
* tests/stress/reflect-get-prototype-of.js: Added.
(shouldBe):
(shouldThrow):
(Base):
(Derived):
* tests/stress/reflect-set-prototype-of.js: Added.
(shouldBe):
(shouldThrow):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@188262 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-get-prototype-of.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/reflect-set-prototype-of.js [new file with mode: 0644]

index 0097f23..4443d4d 100644 (file)
@@ -1,3 +1,34 @@
+2015-08-11  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Implement Reflect.getPrototypeOf and Reflect.setPrototypeOf
+        https://bugs.webkit.org/show_bug.cgi?id=147874
+
+        Reviewed by Darin Adler.
+
+        This patch implements ES6 Reflect.{getPrototypeOf, setPrototypeOf}.
+        The difference from the Object.* one is
+
+        1. They dont not perform ToObject onto the non-object arguments. They make it as a TypeError.
+        2. Reflect.setPrototyeOf returns false when the operation is failed. In Object.setPrototypeOf, it raises a TypeError.
+
+        * runtime/ObjectConstructor.cpp:
+        (JSC::ObjectConstructorGetPrototypeOfFunctor::ObjectConstructorGetPrototypeOfFunctor):
+        (JSC::ObjectConstructorGetPrototypeOfFunctor::result):
+        (JSC::ObjectConstructorGetPrototypeOfFunctor::operator()):
+        (JSC::objectConstructorGetPrototypeOf):
+        * runtime/ObjectConstructor.h:
+        * runtime/ReflectObject.cpp:
+        (JSC::reflectObjectGetPrototypeOf):
+        (JSC::reflectObjectSetPrototypeOf):
+        * tests/stress/reflect-get-prototype-of.js: Added.
+        (shouldBe):
+        (shouldThrow):
+        (Base):
+        (Derived):
+        * tests/stress/reflect-set-prototype-of.js: Added.
+        (shouldBe):
+        (shouldThrow):
+
 2015-08-11  Ting-Wei Lan  <lantw44@gmail.com>
 
         Fix debug build when optimization is enabled
index 9b18a74..814290e 100644 (file)
@@ -152,11 +152,11 @@ public:
     ObjectConstructorGetPrototypeOfFunctor(JSObject* object)
         : m_hasSkippedFirstFrame(false)
         , m_object(object)
-        , m_result(JSValue::encode(jsUndefined()))
+        , m_result(jsUndefined())
     {
     }
 
-    EncodedJSValue result() const { return m_result; }
+    JSValue result() const { return m_result; }
 
     StackVisitor::Status operator()(StackVisitor& visitor)
     {
@@ -166,26 +166,31 @@ public:
         }
 
         if (m_object->allowsAccessFrom(visitor->callFrame()))
-            m_result = JSValue::encode(m_object->prototype());
+            m_result = m_object->prototype();
         return StackVisitor::Done;
     }
 
 private:
     bool m_hasSkippedFirstFrame;
     JSObject* m_object;
-    EncodedJSValue m_result;
+    JSValue m_result;
 };
 
-EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec)
+JSValue objectConstructorGetPrototypeOf(ExecState* exec, JSObject* object)
 {
-    JSObject* object = exec->argument(0).toObject(exec);
-    if (exec->hadException())
-        return JSValue::encode(jsNull());
     ObjectConstructorGetPrototypeOfFunctor functor(object);
     exec->iterate(functor);
     return functor.result();
 }
 
+EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec)
+{
+    JSObject* object = exec->argument(0).toObject(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+    return JSValue::encode(objectConstructorGetPrototypeOf(exec, object));
+}
+
 EncodedJSValue JSC_HOST_CALL objectConstructorSetPrototypeOf(ExecState* exec)
 {
     JSValue objectValue = exec->argument(0);
index 79ec619..1247fc1 100644 (file)
@@ -91,6 +91,7 @@ inline JSObject* constructEmptyObject(ExecState* exec)
 }
 
 JSObject* objectConstructorFreeze(ExecState*, JSObject*);
+JSValue objectConstructorGetPrototypeOf(ExecState*, JSObject*);
 JSArray* ownPropertyKeys(ExecState*, JSObject*, PropertyNameMode, DontEnumPropertiesMode);
 
 } // namespace JSC
index f52c6da..f06bf1f 100644 (file)
@@ -27,6 +27,7 @@
 #include "ReflectObject.h"
 
 #include "JSCInlines.h"
+#include "JSGlobalObjectFunctions.h"
 #include "JSPropertyNameIterator.h"
 #include "Lookup.h"
 #include "ObjectConstructor.h"
 namespace JSC {
 
 static EncodedJSValue JSC_HOST_CALL reflectObjectEnumerate(ExecState*);
+static EncodedJSValue JSC_HOST_CALL reflectObjectGetPrototypeOf(ExecState*);
 static EncodedJSValue JSC_HOST_CALL reflectObjectIsExtensible(ExecState*);
 static EncodedJSValue JSC_HOST_CALL reflectObjectOwnKeys(ExecState*);
 static EncodedJSValue JSC_HOST_CALL reflectObjectPreventExtensions(ExecState*);
+static EncodedJSValue JSC_HOST_CALL reflectObjectSetPrototypeOf(ExecState*);
 
 }
 
@@ -53,9 +56,11 @@ const ClassInfo ReflectObject::s_info = { "Reflect", &Base::s_info, &reflectObje
     apply             reflectObjectApply             DontEnum|Function 3
     deleteProperty    reflectObjectDeleteProperty    DontEnum|Function 2
     enumerate         reflectObjectEnumerate         DontEnum|Function 1
+    getPrototypeOf    reflectObjectGetPrototypeOf    DontEnum|Function 1
     isExtensible      reflectObjectIsExtensible      DontEnum|Function 1
     ownKeys           reflectObjectOwnKeys           DontEnum|Function 1
     preventExtensions reflectObjectPreventExtensions DontEnum|Function 1
+    setPrototypeOf    reflectObjectSetPrototypeOf    DontEnum|Function 2
 @end
 */
 
@@ -85,6 +90,14 @@ EncodedJSValue JSC_HOST_CALL reflectObjectEnumerate(ExecState* exec)
     return JSValue::encode(JSPropertyNameIterator::create(exec, exec->lexicalGlobalObject()->propertyNameIteratorStructure(), asObject(target)));
 }
 
+EncodedJSValue JSC_HOST_CALL reflectObjectGetPrototypeOf(ExecState* exec)
+{
+    JSValue target = exec->argument(0);
+    if (!target.isObject())
+        return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.getPrototypeOf requires the first argument be an object")));
+    return JSValue::encode(objectConstructorGetPrototypeOf(exec, asObject(target)));
+}
+
 EncodedJSValue JSC_HOST_CALL reflectObjectIsExtensible(ExecState* exec)
 {
     JSValue target = exec->argument(0);
@@ -110,4 +123,24 @@ EncodedJSValue JSC_HOST_CALL reflectObjectPreventExtensions(ExecState* exec)
     return JSValue::encode(jsBoolean(true));
 }
 
+EncodedJSValue JSC_HOST_CALL reflectObjectSetPrototypeOf(ExecState* exec)
+{
+    JSValue target = exec->argument(0);
+    if (!target.isObject())
+        return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.setPrototypeOf requires the first argument be an object")));
+    JSValue proto = exec->argument(1);
+    if (!proto.isObject() && !proto.isNull())
+        return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.setPrototypeOf requires the second argument be either an object or null")));
+
+    JSObject* object = asObject(target);
+
+    if (!checkProtoSetterAccessAllowed(exec, object))
+        return JSValue::encode(jsBoolean(false));
+
+    if (!object->isExtensible())
+        return JSValue::encode(jsBoolean(false));
+
+    return JSValue::encode(jsBoolean(object->setPrototypeWithCycleCheck(exec, proto)));
+}
+
 } // namespace JSC
diff --git a/Source/JavaScriptCore/tests/stress/reflect-get-prototype-of.js b/Source/JavaScriptCore/tests/stress/reflect-get-prototype-of.js
new file mode 100644 (file)
index 0000000..15465f1
--- /dev/null
@@ -0,0 +1,50 @@
+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));
+}
+
+shouldBe(Reflect.getPrototypeOf.length, 1);
+
+shouldThrow(() => {
+    Reflect.getPrototypeOf("hello");
+}, `TypeError: Reflect.getPrototypeOf requires the first argument be an object`);
+
+var object = { hello: 42 };
+shouldBe(Reflect.getPrototypeOf(object), Object.prototype);
+shouldBe(Reflect.getPrototypeOf(Reflect.getPrototypeOf(object)), null);
+var proto = [];
+object.__proto__ = proto;
+shouldBe(Reflect.getPrototypeOf(object), proto);
+
+var array = [];
+shouldBe(Reflect.getPrototypeOf(array), Array.prototype);
+var proto = [];
+array.__proto__ = Object.prototype;
+shouldBe(Reflect.getPrototypeOf(array), Object.prototype);
+
+class Base {
+}
+
+class Derived extends Base {
+}
+
+shouldBe(Reflect.getPrototypeOf(new Derived), Derived.prototype);
+shouldBe(Reflect.getPrototypeOf(Reflect.getPrototypeOf(new Derived)), Base.prototype);
+shouldBe(Reflect.getPrototypeOf(Reflect.getPrototypeOf(Reflect.getPrototypeOf(new Derived))), Object.prototype);
+shouldBe(Reflect.getPrototypeOf(Reflect.getPrototypeOf(Reflect.getPrototypeOf(Reflect.getPrototypeOf(new Derived)))), null);
+
+var object = Object.create(null);
+shouldBe(Reflect.getPrototypeOf(object), null);
diff --git a/Source/JavaScriptCore/tests/stress/reflect-set-prototype-of.js b/Source/JavaScriptCore/tests/stress/reflect-set-prototype-of.js
new file mode 100644 (file)
index 0000000..56fc13b
--- /dev/null
@@ -0,0 +1,75 @@
+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));
+}
+
+shouldBe(Reflect.setPrototypeOf.length, 2);
+
+shouldThrow(() => {
+    Reflect.setPrototypeOf("hello");
+}, `TypeError: Reflect.setPrototypeOf requires the first argument be an object`);
+
+shouldThrow(() => {
+    Reflect.setPrototypeOf(null);
+}, `TypeError: Reflect.setPrototypeOf requires the first argument be an object`);
+
+shouldThrow(() => {
+    Reflect.setPrototypeOf({}, 30);
+}, `TypeError: Reflect.setPrototypeOf requires the second argument be either an object or null`);
+
+shouldThrow(() => {
+    Reflect.setPrototypeOf({}, undefined);
+}, `TypeError: Reflect.setPrototypeOf requires the second argument be either an object or null`);
+
+var object = {};
+var prototype = {};
+shouldBe(Reflect.getPrototypeOf(object), Object.prototype);
+shouldBe(Reflect.setPrototypeOf(object, prototype), true);
+shouldBe(Reflect.getPrototypeOf(object), prototype);
+
+var object = {};
+shouldBe(Reflect.getPrototypeOf(object), Object.prototype);
+shouldBe(Reflect.setPrototypeOf(object, null), true);
+shouldBe(Reflect.getPrototypeOf(object), null);
+
+var array = [];
+var prototype = {};
+shouldBe(Reflect.getPrototypeOf(array), Array.prototype);
+shouldBe(Reflect.setPrototypeOf(array, prototype), true);
+shouldBe(Reflect.getPrototypeOf(array), prototype);
+
+var array = [];
+shouldBe(Reflect.getPrototypeOf(array), Array.prototype);
+shouldBe(Reflect.setPrototypeOf(array, null), true);
+shouldBe(Reflect.getPrototypeOf(array), null);
+
+var object = Object.create(null);
+shouldBe(Reflect.getPrototypeOf(object), null);
+shouldBe(Reflect.setPrototypeOf(object, Object.prototype), true);
+shouldBe(Reflect.getPrototypeOf(object), Object.prototype);
+
+// Extensible check.
+var object = {};
+shouldBe(Reflect.preventExtensions(object), true);
+shouldBe(Reflect.setPrototypeOf(object, null), false);
+shouldBe(Reflect.getPrototypeOf(object), Object.prototype);
+
+// Cyclic check.
+var prototype = {};
+var object = { __proto__: prototype };
+shouldBe(Reflect.setPrototypeOf(prototype, object), false);
+shouldBe(Reflect.getPrototypeOf(prototype), Object.prototype);
+