Title: [233918] trunk
Revision
233918
Author
[email protected]
Date
2018-07-18 11:31:09 -0700 (Wed, 18 Jul 2018)

Log Message

[JSC] JSON.stringify's replacer should use `isArray` instead of JSArray checks
https://bugs.webkit.org/show_bug.cgi?id=187755

Reviewed by Mark Lam.

JSTests:

* stress/json-stringify-gap-calculation-should-be-after-replacer-check.js: Added.
(shouldThrow):
(shouldThrow.string.toString):
* test262/expectations.yaml:

Source/_javascript_Core:

JSON.stringify used `inherits<JSArray>(vm)` to determine whether the given replacer is an array replacer.
But this is wrong. According to the spec, we should use `isArray`[1], which accepts Proxies. This difference
makes one test262 test failed.

This patch changes the code to using `isArray()`. And we reorder the evaluations of replacer check and ident space check
to align these checks to the spec's order.

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

* runtime/JSONObject.cpp:
(JSC::Stringifier::Stringifier):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (233917 => 233918)


--- trunk/JSTests/ChangeLog	2018-07-18 18:27:08 UTC (rev 233917)
+++ trunk/JSTests/ChangeLog	2018-07-18 18:31:09 UTC (rev 233918)
@@ -1,3 +1,15 @@
+2018-07-18  Yusuke Suzuki  <[email protected]>
+
+        [JSC] JSON.stringify's replacer should use `isArray` instead of JSArray checks
+        https://bugs.webkit.org/show_bug.cgi?id=187755
+
+        Reviewed by Mark Lam.
+
+        * stress/json-stringify-gap-calculation-should-be-after-replacer-check.js: Added.
+        (shouldThrow):
+        (shouldThrow.string.toString):
+        * test262/expectations.yaml:
+
 2018-07-12  Yusuke Suzuki  <[email protected]>
 
         [JSC] Generator and AsyncGeneratorMethod's prototype is incorrect

Added: trunk/JSTests/stress/json-stringify-gap-calculation-should-be-after-replacer-check.js (0 => 233918)


--- trunk/JSTests/stress/json-stringify-gap-calculation-should-be-after-replacer-check.js	                        (rev 0)
+++ trunk/JSTests/stress/json-stringify-gap-calculation-should-be-after-replacer-check.js	2018-07-18 18:31:09 UTC (rev 233918)
@@ -0,0 +1,33 @@
+var proxy = Proxy.revocable([], {});
+proxy.revoke();
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+shouldThrow(() => {
+    var string = new String("Hello");
+    string.toString = function () {
+        throw new Error("Out");
+    };
+    JSON.stringify({}, proxy.proxy, string);
+}, `TypeError: Array.isArray cannot be called on a Proxy that has been revoked`);
+
+shouldThrow(() => {
+    var string = new String("Hello");
+    string.toString = function () {
+        throw new Error("Out");
+    };
+    JSON.stringify({}, [], string);
+}, `Error: Out`);

Modified: trunk/JSTests/test262/expectations.yaml (233917 => 233918)


--- trunk/JSTests/test262/expectations.yaml	2018-07-18 18:27:08 UTC (rev 233917)
+++ trunk/JSTests/test262/expectations.yaml	2018-07-18 18:31:09 UTC (rev 233918)
@@ -1034,9 +1034,6 @@
 test/built-ins/JSON/parse/reviver-array-length-get-err.js:
   default: 'Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all'
   strict mode: 'Test262Error: Expected a Test262Error to be thrown but no exception was thrown at all'
-test/built-ins/JSON/stringify/replacer-proxy-revoked.js:
-  default: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
-  strict mode: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
 test/built-ins/Map/proto-from-ctor-realm.js:
   default: 'Test262Error: Expected SameValue(«[object Map]», «[object Map]») to be true'
   strict mode: 'Test262Error: Expected SameValue(«[object Map]», «[object Map]») to be true'

Modified: trunk/Source/_javascript_Core/ChangeLog (233917 => 233918)


--- trunk/Source/_javascript_Core/ChangeLog	2018-07-18 18:27:08 UTC (rev 233917)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-07-18 18:31:09 UTC (rev 233918)
@@ -1,5 +1,24 @@
 2018-07-18  Yusuke Suzuki  <[email protected]>
 
+        [JSC] JSON.stringify's replacer should use `isArray` instead of JSArray checks
+        https://bugs.webkit.org/show_bug.cgi?id=187755
+
+        Reviewed by Mark Lam.
+
+        JSON.stringify used `inherits<JSArray>(vm)` to determine whether the given replacer is an array replacer.
+        But this is wrong. According to the spec, we should use `isArray`[1], which accepts Proxies. This difference
+        makes one test262 test failed.
+
+        This patch changes the code to using `isArray()`. And we reorder the evaluations of replacer check and ident space check
+        to align these checks to the spec's order.
+
+        [1]: https://tc39.github.io/ecma262/#sec-json.stringify
+
+        * runtime/JSONObject.cpp:
+        (JSC::Stringifier::Stringifier):
+
+2018-07-18  Yusuke Suzuki  <[email protected]>
+
         [JSC] Root wrapper object in JSON.stringify is not necessary if replacer is not callable
         https://bugs.webkit.org/show_bug.cgi?id=187752
 

Modified: trunk/Source/_javascript_Core/runtime/JSONObject.cpp (233917 => 233918)


--- trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2018-07-18 18:27:08 UTC (rev 233917)
+++ trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2018-07-18 18:31:09 UTC (rev 233918)
@@ -226,37 +226,35 @@
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    m_gap = gap(exec, space);
-    if (UNLIKELY(scope.exception()))
-        return;
+    if (m_replacer.isObject()) {
+        JSObject* replacerObject = asObject(m_replacer);
 
-    if (!m_replacer.isObject())
-        return;
-
-    JSObject* replacerObject = asObject(m_replacer);
-    if (replacerObject->inherits<JSArray>(vm)) {
-        m_usingArrayReplacer = true;
-        JSArray* array = jsCast<JSArray*>(replacerObject);
-        unsigned length = array->get(exec, vm.propertyNames->length).toUInt32(exec);
-        if (UNLIKELY(scope.exception()))
-            return;
-        for (unsigned i = 0; i < length; ++i) {
-            JSValue name = array->get(exec, i);
-            if (UNLIKELY(scope.exception()))
-                break;
-
-            if (auto* nameObject = jsDynamicCast<JSObject*>(vm, name)) {
-                if (!nameObject->inherits<NumberObject>(vm) && !nameObject->inherits<StringObject>(vm))
-                    continue;
-            } else if (!name.isNumber() && !name.isString())
-                continue;
-
-            m_arrayReplacerPropertyNames.add(name.toString(exec)->toIdentifier(exec));
+        m_replacerCallType = CallType::None;
+        if (!replacerObject->isCallable(vm, m_replacerCallType, m_replacerCallData)) {
+            bool isArrayReplacer = JSC::isArray(exec, replacerObject);
+            RETURN_IF_EXCEPTION(scope, );
+            if (isArrayReplacer) {
+                m_usingArrayReplacer = true;
+                unsigned length = replacerObject->get(exec, vm.propertyNames->length).toUInt32(exec);
+                RETURN_IF_EXCEPTION(scope, );
+                for (unsigned i = 0; i < length; ++i) {
+                    JSValue name = replacerObject->get(exec, i);
+                    RETURN_IF_EXCEPTION(scope, );
+                    if (name.isObject()) {
+                        auto* nameObject = jsCast<JSObject*>(name);
+                        if (!nameObject->inherits<NumberObject>(vm) && !nameObject->inherits<StringObject>(vm))
+                            continue;
+                    } else if (!name.isNumber() && !name.isString())
+                        continue;
+                    m_arrayReplacerPropertyNames.add(name.toString(exec)->toIdentifier(exec));
+                    RETURN_IF_EXCEPTION(scope, );
+                }
+            }
         }
-        return;
     }
 
-    m_replacerCallType = replacerObject->methodTable(vm)->getCallData(replacerObject, m_replacerCallData);
+    scope.release();
+    m_gap = gap(exec, space);
 }
 
 JSValue Stringifier::stringify(JSValue value)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to