Title: [197918] trunk/Source/_javascript_Core
Revision
197918
Author
[email protected]
Date
2016-03-09 18:41:34 -0800 (Wed, 09 Mar 2016)

Log Message

Add proper JSON.stringify support for Proxy when the target is an array
https://bugs.webkit.org/show_bug.cgi?id=155180

Reviewed by Darin Adler.

This patch makes the following type of program true:
`JSON.stringify(new Proxy([25], {})) === "[25]"`

We need to change the JSON stringifier to use the IsArray test
in section 7.2.2 of ES6 spec instead of the JSC inherits(JSArray::info())
test.

This patch also adds tests for general JSON.stringify support
of Proxy.

* runtime/ArrayConstructor.cpp:
(JSC::arrayConstructorIsArray):
(JSC::arrayConstructorPrivateFuncIsArrayConstructor):
* runtime/ArrayConstructor.h:
(JSC::isArray):
* runtime/JSONObject.cpp:
(JSC::Stringifier::Holder::object):
(JSC::Stringifier::appendStringifiedValue):
(JSC::Stringifier::startNewLine):
(JSC::Stringifier::Holder::Holder):
* tests/es6.yaml:
* tests/stress/proxy-json.js: Added.
(assert):
(test):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (197917 => 197918)


--- trunk/Source/_javascript_Core/ChangeLog	2016-03-10 02:33:12 UTC (rev 197917)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-03-10 02:41:34 UTC (rev 197918)
@@ -1,3 +1,35 @@
+2016-03-09  Saam barati  <[email protected]>
+
+        Add proper JSON.stringify support for Proxy when the target is an array
+        https://bugs.webkit.org/show_bug.cgi?id=155180
+
+        Reviewed by Darin Adler.
+
+        This patch makes the following type of program true:
+        `JSON.stringify(new Proxy([25], {})) === "[25]"`
+
+        We need to change the JSON stringifier to use the IsArray test
+        in section 7.2.2 of ES6 spec instead of the JSC inherits(JSArray::info())
+        test.
+
+        This patch also adds tests for general JSON.stringify support
+        of Proxy.
+
+        * runtime/ArrayConstructor.cpp:
+        (JSC::arrayConstructorIsArray):
+        (JSC::arrayConstructorPrivateFuncIsArrayConstructor):
+        * runtime/ArrayConstructor.h:
+        (JSC::isArray):
+        * runtime/JSONObject.cpp:
+        (JSC::Stringifier::Holder::object):
+        (JSC::Stringifier::appendStringifiedValue):
+        (JSC::Stringifier::startNewLine):
+        (JSC::Stringifier::Holder::Holder):
+        * tests/es6.yaml:
+        * tests/stress/proxy-json.js: Added.
+        (assert):
+        (test):
+
 2016-03-09  Saam Barati  <[email protected]>
 
         ES6: Implement lexical scoping for function definitions in strict mode

Modified: trunk/Source/_javascript_Core/runtime/ArrayConstructor.cpp (197917 => 197918)


--- trunk/Source/_javascript_Core/runtime/ArrayConstructor.cpp	2016-03-10 02:33:12 UTC (rev 197917)
+++ trunk/Source/_javascript_Core/runtime/ArrayConstructor.cpp	2016-03-10 02:41:34 UTC (rev 197918)
@@ -130,26 +130,7 @@
 // https://tc39.github.io/ecma262/#sec-isarray
 EncodedJSValue JSC_HOST_CALL arrayConstructorIsArray(ExecState* exec)
 {
-    JSValue argumentValue = exec->argument(0);
-
-    if (!argumentValue.isObject())
-        return JSValue::encode(jsBoolean(false));
-
-    JSObject* argument = jsCast<JSObject*>(argumentValue);
-    while (true) {
-        if (argument->inherits(JSArray::info()))
-            return JSValue::encode(jsBoolean(true));
-
-        if (argument->type() != ProxyObjectType)
-            return JSValue::encode(jsBoolean(false));
-
-        ProxyObject* proxy = jsCast<ProxyObject*>(argument);
-        if (proxy->isRevoked())
-            return throwVMTypeError(exec, ASCIILiteral("Array.isArray can not be called on a Proxy that has been revoked."));
-        argument = proxy->target();
-    }
-
-    ASSERT_NOT_REACHED();
+    return JSValue::encode(jsBoolean(isArray(exec, exec->argument(0))));
 }
 
 EncodedJSValue JSC_HOST_CALL arrayConstructorPrivateFuncIsArrayConstructor(ExecState* exec)

Modified: trunk/Source/_javascript_Core/runtime/ArrayConstructor.h (197917 => 197918)


--- trunk/Source/_javascript_Core/runtime/ArrayConstructor.h	2016-03-10 02:33:12 UTC (rev 197917)
+++ trunk/Source/_javascript_Core/runtime/ArrayConstructor.h	2016-03-10 02:41:34 UTC (rev 197918)
@@ -22,6 +22,7 @@
 #define ArrayConstructor_h
 
 #include "InternalFunction.h"
+#include "ProxyObject.h"
 
 namespace JSC {
 
@@ -64,6 +65,32 @@
 
 EncodedJSValue JSC_HOST_CALL arrayConstructorPrivateFuncIsArrayConstructor(ExecState*);
 
+// ES6 7.2.2
+// https://tc39.github.io/ecma262/#sec-isarray
+inline bool isArray(ExecState* exec, JSValue argumentValue)
+{
+    if (!argumentValue.isObject())
+        return false;
+
+    JSObject* argument = jsCast<JSObject*>(argumentValue);
+    while (true) {
+        if (argument->inherits(JSArray::info()))
+            return true;
+
+        if (argument->type() != ProxyObjectType)
+            return false;
+
+        ProxyObject* proxy = jsCast<ProxyObject*>(argument);
+        if (proxy->isRevoked()) {
+            throwTypeError(exec, ASCIILiteral("Array.isArray can not be called on a Proxy that has been revoked."));
+            return false;
+        }
+        argument = proxy->target();
+    }
+
+    ASSERT_NOT_REACHED();
+}
+
 } // namespace JSC
 
 #endif // ArrayConstructor_h

Modified: trunk/Source/_javascript_Core/runtime/JSONObject.cpp (197917 => 197918)


--- trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2016-03-10 02:33:12 UTC (rev 197917)
+++ trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2016-03-10 02:41:34 UTC (rev 197918)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "JSONObject.h"
 
+#include "ArrayConstructor.h"
 #include "BooleanObject.h"
 #include "Error.h"
 #include "ExceptionHelpers.h"
@@ -92,7 +93,7 @@
 private:
     class Holder {
     public:
-        Holder(VM&, JSObject*);
+        Holder(VM&, ExecState*, JSObject*);
 
         JSObject* object() const { return m_object.get(); }
 
@@ -289,9 +290,10 @@
 
 Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName)
 {
+    VM& vm = m_exec->vm();
     // Call the toJSON function.
     value = toJSON(value, propertyName);
-    if (m_exec->hadException())
+    if (vm.exception())
         return StringifyFailed;
 
     // Call the replacer function.
@@ -300,7 +302,7 @@
         args.append(propertyName.value(m_exec));
         args.append(value);
         value = call(m_exec, m_replacer.get(), m_replacerCallType, m_replacerCallData, holder, args);
-        if (m_exec->hadException())
+        if (vm.exception())
             return StringifyFailed;
     }
 
@@ -314,7 +316,7 @@
 
     value = unwrapBoxedPrimitive(m_exec, value);
 
-    if (m_exec->hadException())
+    if (vm.exception())
         return StringifyFailed;
 
     if (value.isBoolean()) {
@@ -364,14 +366,17 @@
             return StringifyFailed;
         }
     }
+
     bool holderStackWasEmpty = m_holderStack.isEmpty();
-    m_holderStack.append(Holder(m_exec->vm(), object));
+    m_holderStack.append(Holder(vm, m_exec, object));
+    if (UNLIKELY(vm.exception()))
+        return StringifyFailed;
     if (!holderStackWasEmpty)
         return StringifySucceeded;
 
     do {
         while (m_holderStack.last().appendNextProperty(*this, builder)) {
-            if (m_exec->hadException())
+            if (vm.exception())
                 return StringifyFailed;
         }
         m_holderStack.removeLast();
@@ -408,9 +413,9 @@
     builder.append(m_indent);
 }
 
-inline Stringifier::Holder::Holder(VM& vm, JSObject* object)
+inline Stringifier::Holder::Holder(VM& vm, ExecState* exec, JSObject* object)
     : m_object(vm, object)
-    , m_isArray(object->inherits(JSArray::info()))
+    , m_isArray(isArray(exec, object))
     , m_index(0)
 #ifndef NDEBUG
     , m_size(0)

Modified: trunk/Source/_javascript_Core/tests/es6.yaml (197917 => 197918)


--- trunk/Source/_javascript_Core/tests/es6.yaml	2016-03-10 02:33:12 UTC (rev 197917)
+++ trunk/Source/_javascript_Core/tests/es6.yaml	2016-03-10 02:41:34 UTC (rev 197918)
@@ -1057,7 +1057,7 @@
 - path: es6/Proxy_isExtensible_handler.js
   cmd: runES6 :normal
 - path: es6/Proxy_JSON.stringify_support.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_ownKeys_handler.js
   cmd: runES6 :normal
 - path: es6/Proxy_preventExtensions_handler.js

Added: trunk/Source/_javascript_Core/tests/stress/proxy-json.js (0 => 197918)


--- trunk/Source/_javascript_Core/tests/stress/proxy-json.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/proxy-json.js	2016-03-10 02:41:34 UTC (rev 197918)
@@ -0,0 +1,114 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad assertion.");
+}
+
+function test(f) {
+    for (let i = 0; i < 500; i++)
+        f();
+}
+
+test(function() {
+    let proxy = new Proxy([], {});
+    assert(JSON.stringify(proxy) === "[]");
+});
+
+test(function() {
+    let target = ["foo"];
+    let proxy = new Proxy(target, {});
+    assert(JSON.stringify(proxy) === JSON.stringify(target));
+});
+
+test(function() {
+    let target = {
+        foo: 25,
+        bar: false,
+        0: 45
+    };
+    let proxy = new Proxy(target, {});
+    assert(JSON.stringify(proxy) === JSON.stringify(target));
+});
+
+test(function() {
+    let target = {
+        foo: ["baz", {foo: 45}],
+        bar: false,
+        0: 45,
+        baz: "hello world",
+        jaz: 4553.434
+    };
+    let proxy = new Proxy(target, {});
+    assert(JSON.stringify(proxy) === JSON.stringify(target));
+});
+
+test(function() {
+    let target = {
+        foo: ["baz", {foo: 45}],
+        bar: false,
+        0: 45,
+        baz: "hello world",
+        jaz: 4553.434
+    };
+    let proxy = new Proxy(target, {});
+    for (let i = 0; i < 50; i++)
+        proxy = new Proxy(proxy, {});
+    assert(JSON.stringify(proxy) === JSON.stringify(target));
+});
+
+test(function() {
+    let target = [20, 30, "foo", {hello: "world"}];
+    let proxy = new Proxy(target, {});
+    for (let i = 0; i < 50; i++)
+        proxy = new Proxy(proxy, {});
+    assert(JSON.stringify(proxy) === JSON.stringify(target));
+});
+
+test(function() {
+    let target = {
+        foo: ["baz", {foo: 45}],
+        bar: false,
+        0: 45,
+        baz: "hello world",
+        jaz: 4553.434
+    };
+    let {proxy, revoke} = Proxy.revocable(target, {});
+    assert(JSON.stringify(proxy) === JSON.stringify(target));
+
+    revoke();
+    JSON.stringify(target); // Things are ok.
+    let threw = false;
+    try {
+        JSON.stringify(proxy); // Things are not ok.
+    } catch(e) {
+        threw = true;
+        assert(e.toString() === "TypeError: Proxy has already been revoked. No more operations are allowed to be performed on it.");
+    }
+    assert(threw);
+});
+
+test(function() {
+    let target = ["foo", "bar", 25, false];
+    let proxy = new Proxy(target, {});
+    let revoke;
+    for (let i = 0; i < 50; i++) {
+        if (i === 25) {
+            let result = Proxy.revocable(proxy, {});
+            proxy = result.proxy;
+            revoke = result.revoke;
+        } else {
+            proxy = new Proxy(proxy, {});
+        }
+    }
+    assert(JSON.stringify(proxy) === JSON.stringify(target));
+
+    revoke();
+    JSON.stringify(target); // Things are ok.
+    let threw = false;
+    try {
+        JSON.stringify(proxy); // Things are not ok.
+    } catch(e) {
+        threw = true;
+        assert(e.toString() === "TypeError: Proxy has already been revoked. No more operations are allowed to be performed on it.");
+    }
+    assert(threw);
+});
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to