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/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);