Title: [233924] trunk
Revision
233924
Author
utatane....@gmail.com
Date
2018-07-18 12:01:34 -0700 (Wed, 18 Jul 2018)

Log Message

JSON.stringify should emit non own properties if second array argument includes
https://bugs.webkit.org/show_bug.cgi?id=187724

Reviewed by Mark Lam.

JSTests:

* stress/json-stringify-getter-call.js: Added.
(shouldBe):
(A.prototype.get cocoa):
(A.prototype.get cappuccino):
(A):
(shouldBe.JSON.stringify):

Source/_javascript_Core:

According to the spec[1], JSON.stringify needs to retrieve properties by using [[Get]],
instead of [[GetOwnProperty]]. It means that we would look up a properties defined
in [[Prototype]] or upper objects in the prototype chain. While enumeration is done
by using EnumerableOwnPropertyNames typically, we can pass replacer array including
property names which does not reside in the own properties. Or we can modify the
own properties by deleting properties while JSON.stringify is calling a getter. So,
using [[Get]] instead of [[GetOwnProperty]] is user-visible.

This patch changes getOwnPropertySlot to getPropertySlot to align the behavior to the spec.
The performance of Kraken/json-stringify-tinderbox is neutral.

[1]: https://tc39.github.io/ecma262/#sec-serializejsonproperty

* runtime/JSONObject.cpp:
(JSC::Stringifier::toJSON):
(JSC::Stringifier::toJSONImpl):
(JSC::Stringifier::appendStringifiedValue):
(JSC::Stringifier::Holder::Holder):
(JSC::Stringifier::Holder::appendNextProperty):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (233923 => 233924)


--- trunk/JSTests/ChangeLog	2018-07-18 19:00:26 UTC (rev 233923)
+++ trunk/JSTests/ChangeLog	2018-07-18 19:01:34 UTC (rev 233924)
@@ -1,5 +1,19 @@
 2018-07-18  Yusuke Suzuki  <utatane....@gmail.com>
 
+        JSON.stringify should emit non own properties if second array argument includes
+        https://bugs.webkit.org/show_bug.cgi?id=187724
+
+        Reviewed by Mark Lam.
+
+        * stress/json-stringify-getter-call.js: Added.
+        (shouldBe):
+        (A.prototype.get cocoa):
+        (A.prototype.get cappuccino):
+        (A):
+        (shouldBe.JSON.stringify):
+
+2018-07-18  Yusuke Suzuki  <utatane....@gmail.com>
+
         [JSC] JSON.stringify's replacer should use `isArray` instead of JSArray checks
         https://bugs.webkit.org/show_bug.cgi?id=187755
 

Added: trunk/JSTests/stress/json-stringify-getter-call.js (0 => 233924)


--- trunk/JSTests/stress/json-stringify-getter-call.js	                        (rev 0)
+++ trunk/JSTests/stress/json-stringify-getter-call.js	2018-07-18 19:01:34 UTC (rev 233924)
@@ -0,0 +1,32 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+class A {
+    get cocoa() {
+        return "Cocoa";
+    }
+
+    get cappuccino() {
+        return "Cappuccino";
+    }
+}
+
+let a = new A();
+shouldBe(JSON.stringify(a), `{}`);
+shouldBe(JSON.stringify(a, ["cocoa", "cappuccino"]), `{"cocoa":"Cocoa","cappuccino":"Cappuccino"}`);
+
+let array = [0, 1, 2, 3, 4];
+Object.defineProperty(array.__proto__, 1, {
+    get: function () {
+        return "Cocoa";
+    }
+});
+Object.defineProperty(array, 0, {
+    get: function () {
+        delete array[1];
+        return "Cappuccino";
+    }
+});
+shouldBe(JSON.stringify(array), `["Cappuccino","Cocoa",2,3,4]`);

Modified: trunk/Source/_javascript_Core/ChangeLog (233923 => 233924)


--- trunk/Source/_javascript_Core/ChangeLog	2018-07-18 19:00:26 UTC (rev 233923)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-07-18 19:01:34 UTC (rev 233924)
@@ -1,5 +1,32 @@
 2018-07-18  Yusuke Suzuki  <utatane....@gmail.com>
 
+        JSON.stringify should emit non own properties if second array argument includes
+        https://bugs.webkit.org/show_bug.cgi?id=187724
+
+        Reviewed by Mark Lam.
+
+        According to the spec[1], JSON.stringify needs to retrieve properties by using [[Get]],
+        instead of [[GetOwnProperty]]. It means that we would look up a properties defined
+        in [[Prototype]] or upper objects in the prototype chain. While enumeration is done
+        by using EnumerableOwnPropertyNames typically, we can pass replacer array including
+        property names which does not reside in the own properties. Or we can modify the
+        own properties by deleting properties while JSON.stringify is calling a getter. So,
+        using [[Get]] instead of [[GetOwnProperty]] is user-visible.
+
+        This patch changes getOwnPropertySlot to getPropertySlot to align the behavior to the spec.
+        The performance of Kraken/json-stringify-tinderbox is neutral.
+
+        [1]: https://tc39.github.io/ecma262/#sec-serializejsonproperty
+
+        * runtime/JSONObject.cpp:
+        (JSC::Stringifier::toJSON):
+        (JSC::Stringifier::toJSONImpl):
+        (JSC::Stringifier::appendStringifiedValue):
+        (JSC::Stringifier::Holder::Holder):
+        (JSC::Stringifier::Holder::appendNextProperty):
+
+2018-07-18  Yusuke Suzuki  <utatane....@gmail.com>
+
         [JSC] JSON.stringify's replacer should use `isArray` instead of JSArray checks
         https://bugs.webkit.org/show_bug.cgi?id=187755
 

Modified: trunk/Source/_javascript_Core/runtime/JSONObject.cpp (233923 => 233924)


--- trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2018-07-18 19:00:26 UTC (rev 233923)
+++ trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2018-07-18 19:01:34 UTC (rev 233924)
@@ -101,17 +101,17 @@
 
     private:
         JSObject* m_object;
+        const bool m_isJSArray;
         const bool m_isArray;
-        const bool m_isJSArray;
-        unsigned m_index;
-        unsigned m_size;
+        unsigned m_index { 0 };
+        unsigned m_size { 0 };
         RefPtr<PropertyNameArrayData> m_propertyNames;
     };
 
     friend class Holder;
 
-    JSValue toJSON(JSValue, const PropertyNameForFunctionCall&);
-    JSValue toJSONImpl(VM&, JSValue, JSValue toJSONFunction, const PropertyNameForFunctionCall&);
+    JSValue toJSON(JSObject*, const PropertyNameForFunctionCall&);
+    JSValue toJSONImpl(VM&, JSObject*, JSValue toJSONFunction, const PropertyNameForFunctionCall&);
 
     enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedOrSymbolValue };
     StringifyResult appendStringifiedValue(StringBuilder&, JSValue, const Holder&, const PropertyNameForFunctionCall&);
@@ -284,38 +284,35 @@
     return jsString(m_exec, result.toString());
 }
 
-ALWAYS_INLINE JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionCall& propertyName)
+ALWAYS_INLINE JSValue Stringifier::toJSON(JSObject* object, const PropertyNameForFunctionCall& propertyName)
 {
     VM& vm = m_exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
     scope.assertNoException();
-    if (!value.isObject())
-        return value;
-    
-    JSObject* object = asObject(value);
+
     PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
     bool hasProperty = object->getPropertySlot(m_exec, vm.propertyNames->toJSON, slot);
     EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
     if (!hasProperty)
-        return value;
+        return object;
 
     JSValue toJSONFunction = slot.getValue(m_exec, vm.propertyNames->toJSON);
     RETURN_IF_EXCEPTION(scope, { });
     scope.release();
-    return toJSONImpl(vm, value, toJSONFunction, propertyName);
+    return toJSONImpl(vm, object, toJSONFunction, propertyName);
 }
 
-JSValue Stringifier::toJSONImpl(VM& vm, JSValue value, JSValue toJSONFunction, const PropertyNameForFunctionCall& propertyName)
+JSValue Stringifier::toJSONImpl(VM& vm, JSObject* object, JSValue toJSONFunction, const PropertyNameForFunctionCall& propertyName)
 {
     CallType callType;
     CallData callData;
     if (!toJSONFunction.isCallable(vm, callType, callData))
-        return value;
+        return object;
 
     MarkedArgumentBuffer args;
     args.append(propertyName.value(m_exec));
     ASSERT(!args.hasOverflowed());
-    return call(m_exec, asObject(toJSONFunction), callType, callData, value, args);
+    return call(m_exec, asObject(toJSONFunction), callType, callData, object, args);
 }
 
 Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, const Holder& holder, const PropertyNameForFunctionCall& propertyName)
@@ -324,8 +321,10 @@
     auto scope = DECLARE_THROW_SCOPE(vm);
 
     // Call the toJSON function.
-    value = toJSON(value, propertyName);
-    RETURN_IF_EXCEPTION(scope, StringifyFailed);
+    if (value.isObject()) {
+        value = toJSON(asObject(value), propertyName);
+        RETURN_IF_EXCEPTION(scope, StringifyFailed);
+    }
 
     // Call the replacer function.
     if (isCallableReplacer()) {
@@ -448,23 +447,15 @@
 
 inline Stringifier::Holder::Holder(ExecState* exec, JSObject* object)
     : m_object(object)
+    , m_isJSArray(isJSArray(object))
     , m_isArray(JSC::isArray(exec, object))
-    , m_isJSArray(m_isArray && isJSArray(object))
-    , m_index(0)
-#ifndef NDEBUG
-    , m_size(0)
-#endif
 {
 }
 
 inline Stringifier::Holder::Holder(RootHolderTag, JSObject* object)
     : m_object(object)
+    , m_isJSArray(false)
     , m_isArray(false)
-    , m_isJSArray(false)
-    , m_index(0)
-#ifndef NDEBUG
-    , m_size(0)
-#endif
 {
 }
 
@@ -523,7 +514,7 @@
             value = asArray(m_object)->getIndexQuickly(index);
         else {
             PropertySlot slot(m_object, PropertySlot::InternalMethodType::Get);
-            if (m_object->methodTable(vm)->getOwnPropertySlotByIndex(m_object, exec, index, slot))
+            if (m_object->getPropertySlot(exec, index, slot))
                 value = slot.getValue(exec, index);
             else
                 value = jsUndefined();
@@ -542,7 +533,7 @@
         // Get the value.
         PropertySlot slot(m_object, PropertySlot::InternalMethodType::Get);
         Identifier& propertyName = m_propertyNames->propertyNameVector()[index];
-        if (!m_object->methodTable(vm)->getOwnPropertySlot(m_object, exec, propertyName, slot))
+        if (!m_object->getPropertySlot(exec, propertyName, slot))
             return true;
         JSValue value = slot.getValue(exec, propertyName);
         RETURN_IF_EXCEPTION(scope, false);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to