<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[198150] trunk/Source/JavaScriptCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/198150">198150</a></dd>
<dt>Author</dt> <dd>sbarati@apple.com</dd>
<dt>Date</dt> <dd>2016-03-14 13:07:16 -0700 (Mon, 14 Mar 2016)</dd>
</dl>
<h3>Log Message</h3>
<pre>[ES6] Make JSON.stringify ES6 compatible
https://bugs.webkit.org/show_bug.cgi?id=155448
Reviewed by Sam Weinig and Mark Lam.
We weren't following the spec with respect to the "toJSON" property
of the thing being stringified. We were perform hasProperty(.)
on "toJSON" instead of get(.). This patch changes it our
implementation to perform get(value, "toJSON").
* runtime/JSCJSValue.h:
* runtime/JSCJSValueInlines.h:
(JSC::JSValue::isFunction):
(JSC::JSValue::isCallable):
* runtime/JSONObject.cpp:
(JSC::Stringifier::toJSON):
(JSC::Stringifier::toJSONImpl):
(JSC::Stringifier::appendStringifiedValue):
* tests/es6.yaml:
* tests/stress/proxy-json.js:
(test):
(test.let.handler.get assert):
(test.let.handler):</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSCJSValueh">trunk/Source/JavaScriptCore/runtime/JSCJSValue.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSCJSValueInlinesh">trunk/Source/JavaScriptCore/runtime/JSCJSValueInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSONObjectcpp">trunk/Source/JavaScriptCore/runtime/JSONObject.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoretestses6yaml">trunk/Source/JavaScriptCore/tests/es6.yaml</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressproxyjsonjs">trunk/Source/JavaScriptCore/tests/stress/proxy-json.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (198149 => 198150)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-03-14 20:06:54 UTC (rev 198149)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-03-14 20:07:16 UTC (rev 198150)
</span><span class="lines">@@ -1,5 +1,31 @@
</span><span class="cx"> 2016-03-14 Saam barati <sbarati@apple.com>
</span><span class="cx">
</span><ins>+ [ES6] Make JSON.stringify ES6 compatible
+ https://bugs.webkit.org/show_bug.cgi?id=155448
+
+ Reviewed by Sam Weinig and Mark Lam.
+
+ We weren't following the spec with respect to the "toJSON" property
+ of the thing being stringified. We were perform hasProperty(.)
+ on "toJSON" instead of get(.). This patch changes it our
+ implementation to perform get(value, "toJSON").
+
+ * runtime/JSCJSValue.h:
+ * runtime/JSCJSValueInlines.h:
+ (JSC::JSValue::isFunction):
+ (JSC::JSValue::isCallable):
+ * runtime/JSONObject.cpp:
+ (JSC::Stringifier::toJSON):
+ (JSC::Stringifier::toJSONImpl):
+ (JSC::Stringifier::appendStringifiedValue):
+ * tests/es6.yaml:
+ * tests/stress/proxy-json.js:
+ (test):
+ (test.let.handler.get assert):
+ (test.let.handler):
+
+2016-03-14 Saam barati <sbarati@apple.com>
+
</ins><span class="cx"> [ES6] Disallow var assignments in for-in loops
</span><span class="cx"> https://bugs.webkit.org/show_bug.cgi?id=155451
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSCJSValueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSCJSValue.h (198149 => 198150)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSCJSValue.h        2016-03-14 20:06:54 UTC (rev 198149)
+++ trunk/Source/JavaScriptCore/runtime/JSCJSValue.h        2016-03-14 20:07:16 UTC (rev 198150)
</span><span class="lines">@@ -225,6 +225,7 @@
</span><span class="cx"> bool isEmpty() const;
</span><span class="cx"> bool isFunction() const;
</span><span class="cx"> bool isFunction(CallType&, CallData&) const;
</span><ins>+ bool isCallable(CallType&, CallData&) const;
</ins><span class="cx"> bool isConstructor() const;
</span><span class="cx"> bool isConstructor(ConstructType&, ConstructData&) const;
</span><span class="cx"> bool isUndefined() const;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSCJSValueInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSCJSValueInlines.h (198149 => 198150)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSCJSValueInlines.h        2016-03-14 20:06:54 UTC (rev 198149)
+++ trunk/Source/JavaScriptCore/runtime/JSCJSValueInlines.h        2016-03-14 20:07:16 UTC (rev 198150)
</span><span class="lines">@@ -710,6 +710,11 @@
</span><span class="cx">
</span><span class="cx"> inline bool JSValue::isFunction(CallType& callType, CallData& callData) const
</span><span class="cx"> {
</span><ins>+ return isCallable(callType, callData);
+}
+
+inline bool JSValue::isCallable(CallType& callType, CallData& callData) const
+{
</ins><span class="cx"> if (!isCell())
</span><span class="cx"> return false;
</span><span class="cx"> JSCell* cell = asCell();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSONObjectcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSONObject.cpp (198149 => 198150)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSONObject.cpp        2016-03-14 20:06:54 UTC (rev 198149)
+++ trunk/Source/JavaScriptCore/runtime/JSONObject.cpp        2016-03-14 20:07:16 UTC (rev 198150)
</span><span class="lines">@@ -111,7 +111,7 @@
</span><span class="cx"> friend class Holder;
</span><span class="cx">
</span><span class="cx"> JSValue toJSON(JSValue, const PropertyNameForFunctionCall&);
</span><del>- JSValue toJSONImpl(JSValue, const PropertyNameForFunctionCall&);
</del><ins>+ JSValue toJSONImpl(JSValue value, JSValue toJSONFunction, const PropertyNameForFunctionCall&);
</ins><span class="cx">
</span><span class="cx"> enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedOrSymbolValue };
</span><span class="cx"> StringifyResult appendStringifiedValue(StringBuilder&, JSValue, JSObject* holder, const PropertyNameForFunctionCall&);
</span><span class="lines">@@ -263,29 +263,31 @@
</span><span class="cx"> ALWAYS_INLINE JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionCall& propertyName)
</span><span class="cx"> {
</span><span class="cx"> ASSERT(!m_exec->hadException());
</span><del>- if (!value.isObject() || !asObject(value)->hasProperty(m_exec, m_exec->vm().propertyNames->toJSON))
</del><ins>+ if (!value.isObject())
</ins><span class="cx"> return value;
</span><del>- return toJSONImpl(value, propertyName);
</del><ins>+
+ JSObject* object = asObject(value);
+ VM& vm = m_exec->vm();
+ PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
+ if (!object->getPropertySlot(m_exec, vm.propertyNames->toJSON, slot))
+ return value;
+
+ JSValue toJSONFunction = slot.getValue(m_exec, vm.propertyNames->toJSON);
+ if (vm.exception())
+ return jsNull();
+ return toJSONImpl(value, toJSONFunction, propertyName);
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-JSValue Stringifier::toJSONImpl(JSValue value, const PropertyNameForFunctionCall& propertyName)
</del><ins>+JSValue Stringifier::toJSONImpl(JSValue value, JSValue toJSONFunction, const PropertyNameForFunctionCall& propertyName)
</ins><span class="cx"> {
</span><del>- JSValue toJSONFunction = asObject(value)->get(m_exec, m_exec->vm().propertyNames->toJSON);
- if (m_exec->hadException())
- return jsNull();
-
- if (!toJSONFunction.isObject())
- return value;
-
- JSObject* object = asObject(toJSONFunction);
</del><ins>+ CallType callType;
</ins><span class="cx"> CallData callData;
</span><del>- CallType callType = object->methodTable()->getCallData(object, callData);
- if (callType == CallType::None)
</del><ins>+ if (!toJSONFunction.isCallable(callType, callData))
</ins><span class="cx"> return value;
</span><span class="cx">
</span><span class="cx"> MarkedArgumentBuffer args;
</span><span class="cx"> args.append(propertyName.value(m_exec));
</span><del>- return call(m_exec, object, callType, callData, value, args);
</del><ins>+ return call(m_exec, asObject(toJSONFunction), callType, callData, value, args);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestses6yaml"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tests/es6.yaml (198149 => 198150)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/es6.yaml        2016-03-14 20:06:54 UTC (rev 198149)
+++ trunk/Source/JavaScriptCore/tests/es6.yaml        2016-03-14 20:07:16 UTC (rev 198150)
</span><span class="lines">@@ -983,7 +983,7 @@
</span><span class="cx"> - path: es6/Proxy_internal_get_calls_IteratorComplete_IteratorValue.js
</span><span class="cx"> cmd: runES6 :normal
</span><span class="cx"> - path: es6/Proxy_internal_get_calls_JSON.stringify.js
</span><del>- cmd: runES6 :fail
</del><ins>+ cmd: runES6 :normal
</ins><span class="cx"> - path: es6/Proxy_internal_get_calls_Object.assign.js
</span><span class="cx"> cmd: runES6 :normal
</span><span class="cx"> - path: es6/Proxy_internal_get_calls_Object.defineProperties.js
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressproxyjsonjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tests/stress/proxy-json.js (198149 => 198150)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/proxy-json.js        2016-03-14 20:06:54 UTC (rev 198149)
+++ trunk/Source/JavaScriptCore/tests/stress/proxy-json.js        2016-03-14 20:07:16 UTC (rev 198150)
</span><span class="lines">@@ -112,3 +112,50 @@
</span><span class="cx"> }
</span><span class="cx"> assert(threw);
</span><span class="cx"> });
</span><ins>+
+test(function() {
+ let arr = ["foo", 25, "bar"];
+ let handler = {
+ get: function(theTarget, propName) {
+ assert(propName === "toJSON");
+ return function() {
+ return arr;
+ }
+ }
+ };
+ let proxy = new Proxy({}, handler);
+ assert(JSON.stringify(proxy) === JSON.stringify(arr));
+});
+
+test(function() {
+ let arr = ["foo", 25, "bar"];
+ let handler = {
+ get: function(theTarget, propName) {
+ assert(propName === "toJSON");
+ return function() {
+ return arr;
+ }
+ }
+ };
+ let proxy = new Proxy({}, handler);
+ let o1 = {foo: arr};
+ let o2 = {foo: proxy};
+ assert(JSON.stringify(o1) === JSON.stringify(o2));
+});
+
+test(function() {
+ let arr = ["foo", 25, "bar"];
+ let proxy = new Proxy(function() { return arr; }, {});
+ assert(JSON.stringify({toJSON: proxy}) === JSON.stringify(arr));
+});
+
+test(function() {
+ let arr = ["foo", 25, "bar"];
+ let proxy = new Proxy({}, {});
+ let o = {foo: 20};
+ Object.defineProperty(o, "toJSON", {
+ enumerable: false,
+ value: proxy
+ });
+ assert(JSON.stringify(o) === JSON.stringify({foo: 20}));
+});
</ins></span></pre>
</div>
</div>
</body>
</html>