[ES6] Implement Reflect.get
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 17 Aug 2015 18:56:52 +0000 (18:56 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 17 Aug 2015 18:56:52 +0000 (18:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=147925

Reviewed by Geoffrey Garen.

This patch implements Reflect.get API.
It can take the receiver object as the third argument.
When the receiver is specified and there's a getter for the given property name,
we call the getter with the receiver as the |this| value.

* runtime/ReflectObject.cpp:
(JSC::reflectObjectGet):
* runtime/SparseArrayValueMap.cpp:
(JSC::SparseArrayEntry::get): Deleted.
* runtime/SparseArrayValueMap.h:
* tests/stress/reflect-get.js: Added.
(shouldBe):
(shouldThrow):
(.get shouldThrow):
(.get var):
(get var.object.get hello):
(.get shouldBe):
(get var.object.set hello):

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

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

index 8123771..dffbc4f 100644 (file)
@@ -1,3 +1,29 @@
+2015-08-17  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Implement Reflect.get
+        https://bugs.webkit.org/show_bug.cgi?id=147925
+
+        Reviewed by Geoffrey Garen.
+
+        This patch implements Reflect.get API.
+        It can take the receiver object as the third argument.
+        When the receiver is specified and there's a getter for the given property name,
+        we call the getter with the receiver as the |this| value.
+
+        * runtime/ReflectObject.cpp:
+        (JSC::reflectObjectGet):
+        * runtime/SparseArrayValueMap.cpp:
+        (JSC::SparseArrayEntry::get): Deleted.
+        * runtime/SparseArrayValueMap.h:
+        * tests/stress/reflect-get.js: Added.
+        (shouldBe):
+        (shouldThrow):
+        (.get shouldThrow):
+        (.get var):
+        (get var.object.get hello):
+        (.get shouldBe):
+        (get var.object.set hello):
+
 2015-08-17  Simon Fraser  <simon.fraser@apple.com>
 
         will-change should sometimes trigger compositing
index 2a5bace..9f9a97e 100644 (file)
@@ -36,6 +36,7 @@ namespace JSC {
 
 static EncodedJSValue JSC_HOST_CALL reflectObjectDefineProperty(ExecState*);
 static EncodedJSValue JSC_HOST_CALL reflectObjectEnumerate(ExecState*);
+static EncodedJSValue JSC_HOST_CALL reflectObjectGet(ExecState*);
 static EncodedJSValue JSC_HOST_CALL reflectObjectGetOwnPropertyDescriptor(ExecState*);
 static EncodedJSValue JSC_HOST_CALL reflectObjectGetPrototypeOf(ExecState*);
 static EncodedJSValue JSC_HOST_CALL reflectObjectIsExtensible(ExecState*);
@@ -59,6 +60,7 @@ const ClassInfo ReflectObject::s_info = { "Reflect", &Base::s_info, &reflectObje
     defineProperty           reflectObjectDefineProperty           DontEnum|Function 3
     deleteProperty           reflectObjectDeleteProperty           DontEnum|Function 2
     enumerate                reflectObjectEnumerate                DontEnum|Function 1
+    get                      reflectObjectGet                      DontEnum|Function 2
     getOwnPropertyDescriptor reflectObjectGetOwnPropertyDescriptor DontEnum|Function 2
     getPrototypeOf           reflectObjectGetPrototypeOf           DontEnum|Function 1
     has                      reflectObjectHas                      DontEnum|Function 2
@@ -118,6 +120,25 @@ EncodedJSValue JSC_HOST_CALL reflectObjectEnumerate(ExecState* exec)
     return JSValue::encode(JSPropertyNameIterator::create(exec, exec->lexicalGlobalObject()->propertyNameIteratorStructure(), asObject(target)));
 }
 
+// http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.get
+EncodedJSValue JSC_HOST_CALL reflectObjectGet(ExecState* exec)
+{
+    JSValue target = exec->argument(0);
+    if (!target.isObject())
+        return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.get requires the first argument be an object")));
+
+    const Identifier propertyName = exec->argument(1).toPropertyKey(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsNull());
+
+    JSValue receiver = target;
+    if (exec->argumentCount() >= 3)
+        receiver = exec->argument(2);
+
+    PropertySlot slot(receiver);
+    return JSValue::encode(target.get(exec, propertyName, slot));
+}
+
 // http://www.ecma-international.org/ecma-262/6.0/#sec-reflect.getownpropertydescriptor
 EncodedJSValue JSC_HOST_CALL reflectObjectGetOwnPropertyDescriptor(ExecState* exec)
 {
index 62d34ba..b395984 100644 (file)
@@ -142,17 +142,6 @@ void SparseArrayEntry::get(PropertyDescriptor& descriptor) const
     descriptor.setDescriptor(Base::get(), attributes);
 }
 
-JSValue SparseArrayEntry::get(ExecState* exec, JSObject* array) const
-{
-    JSValue value = Base::get();
-    ASSERT(value);
-
-    if (LIKELY(!value.isGetterSetter()))
-        return value;
-
-    return callGetter(exec, array, jsCast<GetterSetter*>(value));
-}
-
 void SparseArrayEntry::put(ExecState* exec, JSValue thisValue, SparseArrayValueMap* map, JSValue value, bool shouldThrow)
 {
     if (!(attributes & Accessor)) {
index ff36caa..0754b53 100644 (file)
@@ -42,7 +42,6 @@ struct SparseArrayEntry : public WriteBarrier<Unknown> {
 
     SparseArrayEntry() : attributes(0) { }
 
-    JSValue get(ExecState*, JSObject*) const;
     void get(JSObject*, PropertySlot&) const;
     void get(PropertyDescriptor&) const;
     void put(ExecState*, JSValue thisValue, SparseArrayValueMap*, JSValue, bool shouldThrow);
diff --git a/Source/JavaScriptCore/tests/stress/reflect-get.js b/Source/JavaScriptCore/tests/stress/reflect-get.js
new file mode 100644 (file)
index 0000000..48419bd
--- /dev/null
@@ -0,0 +1,91 @@
+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.get.length, 2);
+
+shouldThrow(() => {
+    Reflect.get("hello");
+}, `TypeError: Reflect.get requires the first argument be an object`);
+
+var object = { hello: 42 };
+shouldBe(Reflect.get(object, 'hello'), 42);
+shouldBe(Reflect.get(object, 'world'), undefined);
+var proto = [];
+object.__proto__ = proto;
+shouldBe(Reflect.get(object, 'length'), 0);
+
+var array = [ 0, 1, 2 ];
+shouldBe(Reflect.get(array, 0), 0);
+var proto = [ 0, 1, 2, 5, 6 ];
+array.__proto__ = proto;
+shouldBe(Reflect.get(array, 3), 5);
+array.__proto__ = Object.prototype;
+shouldBe(Reflect.get(array, 3), undefined);
+
+var object = {
+    value: 42,
+    world: 200,
+    get hello()
+    {
+        return this.value;
+    }
+};
+shouldBe(Reflect.get(object, 'hello'), 42);
+shouldBe(Reflect.get(object, 'hello', { value: 200 }), 200);
+shouldBe(Reflect.get(object, 'hello', "OK"), undefined);
+shouldBe(Reflect.get(object, 'world'), 200);
+shouldBe(Reflect.get(object, 'world', { value: 200 }), 200);
+shouldBe(Reflect.get(object, 'world', "OK"), 200);
+var value = 400;
+shouldBe(Reflect.get(object, 'hello', null), 400);
+shouldBe(Reflect.get(object, 'hello', undefined), 400);
+
+var object = {
+    value: 42,
+    world: 200,
+    get hello()
+    {
+        "use strict";
+        return this.value;
+    }
+};
+shouldBe(Reflect.get(object, 'hello'), 42);
+shouldBe(Reflect.get(object, 'hello', { value: 200 }), 200);
+shouldBe(Reflect.get(object, 'hello', "OK"), undefined);
+shouldBe(Reflect.get(object, 'world'), 200);
+shouldBe(Reflect.get(object, 'world', { value: 200 }), 200);
+shouldBe(Reflect.get(object, 'world', "OK"), 200);
+
+shouldThrow(() => {
+    Reflect.get(object, 'hello', null);
+}, `TypeError: null is not an object (evaluating 'this.value')`);
+
+shouldThrow(() => {
+    Reflect.get(object, 'hello', undefined);
+}, `TypeError: undefined is not an object (evaluating 'this.value')`);
+
+var object = {
+    value: 42,
+    world: 200,
+    set hello(value)
+    {
+    }
+};
+shouldBe(Reflect.get(object, 'hello'), undefined);
+shouldBe(Reflect.get(object, 'hello', { hello: 42 }), undefined);
+shouldBe(Reflect.get(object, 'ok', { ok: 42 }), undefined);