Commit 82d7eb12 authored by oliver@apple.com's avatar oliver@apple.com

Revert r44521 as it causes regressions on windows for some reason.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@44522 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent e6ad0f1c
2009-06-07 Oliver Hunt <oliver@apple.com>
Reviewed by Sam Weinig.
Bug 26249: Support JSON.stringify
<https://bugs.webkit.org/show_bug.cgi?id=26249>
Implement JSON.stringify. This patch handles all the semantics of the ES5
JSON.stringify function, including replacer functions and arrays and both
string and numeric gap arguments.
Currently uses a clamped recursive algorithm basically identical to the spec
description but with a few minor tweaks for performance and corrected semantics
discussed in the es-discuss mailing list.
* DerivedSources.make:
* GNUmakefile.am:
* JavaScriptCore.pri:
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* interpreter/CallFrame.h:
(JSC::ExecState::jsonTable):
* runtime/CommonIdentifiers.h:
add toJSON to the list of common identifiers
* runtime/JSGlobalData.cpp:
(JSC::JSGlobalData::JSGlobalData):
(JSC::JSGlobalData::~JSGlobalData):
* runtime/JSGlobalData.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::reset):
Add support for the JSON object lookup table
* runtime/JSONObject.cpp: Added.
(JSC::):
(JSC::JSONObject::getOwnPropertySlot):
(JSC::Stringifier::):
(JSC::Stringifier::Stringifier):
(JSC::Stringifier::stringify):
(JSC::Stringifier::appendString):
(JSC::Stringifier::StringKeyGenerator::StringKeyGenerator):
(JSC::Stringifier::StringKeyGenerator::getKey):
(JSC::Stringifier::IntKeyGenerator::IntKeyGenerator):
(JSC::Stringifier::IntKeyGenerator::getKey):
These KeyGenerator classes are used to abstract away the lazy evaluation of keys for
toJSON and replacer functions.
(JSC::Stringifier::toJSONValue):
(JSC::Stringifier::stringifyArray):
(JSC::Stringifier::stringifyObject):
(JSC::JSONProtoFuncStringify):
* runtime/JSONObject.h: Added.
(JSC::JSONObject:::JSObject):
(JSC::JSONObject::classInfo):
(JSC::JSONObject::createStructure):
2009-06-08 Mark Rowe <mrowe@apple.com>
Speculative GTK build fix.
......@@ -40,7 +40,6 @@ all : \
chartables.c \
DatePrototype.lut.h \
Grammar.cpp \
JSONObject.lut.h \
Lexer.lut.h \
MathObject.lut.h \
NumberConstructor.lut.h \
......
......@@ -35,7 +35,6 @@ javascriptcore_built_nosources += \
DerivedSources/Lexer.lut.h \
JavaScriptCore/runtime/ArrayPrototype.lut.h \
JavaScriptCore/runtime/DatePrototype.lut.h \
JavaScriptCore/runtime/JSONObject.lut.h \
JavaScriptCore/runtime/MathObject.lut.h \
JavaScriptCore/runtime/NumberConstructor.lut.h \
JavaScriptCore/runtime/RegExpConstructor.lut.h \
......@@ -183,8 +182,6 @@ javascriptcore_sources += \
JavaScriptCore/runtime/JSGlobalData.h \
JavaScriptCore/runtime/JSNotAnObject.cpp \
JavaScriptCore/runtime/JSNotAnObject.h \
JavaScriptCore/runtime/JSONObject.cpp \
JavaScriptCore/runtime/JSONObject.h \
JavaScriptCore/runtime/JSPropertyNameIterator.cpp \
JavaScriptCore/runtime/JSPropertyNameIterator.h \
JavaScriptCore/runtime/LiteralParser.cpp \
......@@ -593,7 +590,6 @@ javascriptcore_dist += \
CLEANFILES += \
JavaScriptCore/runtime/ArrayPrototype.lut.h \
JavaScriptCore/runtime/DatePrototype.lut.h \
JavaScriptCore/runtime/JSONObject.lut.h \
JavaScriptCore/runtime/MathObject.lut.h \
JavaScriptCore/runtime/NumberConstructor.lut.h \
JavaScriptCore/runtime/RegExpConstructor.lut.h \
......
......@@ -110,7 +110,6 @@ SOURCES += \
runtime/JSVariableObject.cpp \
runtime/JSActivation.cpp \
runtime/JSNotAnObject.cpp \
runtime/JSONObject.cpp \
runtime/LiteralParser.cpp \
runtime/TimeoutChecker.cpp \
bytecode/CodeBlock.cpp \
......
......@@ -794,13 +794,6 @@
<File
RelativePath="..\..\runtime\JSNumberCell.h"
>
<File
RelativePath="..\..\runtime\JSONObject.cpp"
>
</File>
<File
RelativePath="..\..\runtime\JSONObject.h"
>
</File>
<File
RelativePath="..\..\runtime\JSObject.cpp"
......
......@@ -198,9 +198,6 @@
A7B48F490EE8936F00DCBDB6 /* ExecutableAllocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7B48DB60EE74CFC00DCBDB6 /* ExecutableAllocator.cpp */; };
A7E2EA6B0FB460CF00601F06 /* LiteralParser.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E2EA690FB460CF00601F06 /* LiteralParser.h */; };
A7E2EA6C0FB460CF00601F06 /* LiteralParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7E2EA6A0FB460CF00601F06 /* LiteralParser.cpp */; };
A7F9935F0FD7325100A0B2D0 /* JSONObject.h in Headers */ = {isa = PBXBuildFile; fileRef = A7F9935D0FD7325100A0B2D0 /* JSONObject.h */; };
A7F993600FD7325100A0B2D0 /* JSONObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7F9935E0FD7325100A0B2D0 /* JSONObject.cpp */; };
A7F9949B0FD746A300A0B2D0 /* JSONObject.lut.h in Headers */ = {isa = PBXBuildFile; fileRef = A7F9949A0FD746A300A0B2D0 /* JSONObject.lut.h */; };
BC02E90D0E1839DB000F9297 /* ErrorConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = BC02E9050E1839DB000F9297 /* ErrorConstructor.h */; };
BC02E90F0E1839DB000F9297 /* ErrorPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = BC02E9070E1839DB000F9297 /* ErrorPrototype.h */; };
BC02E9110E1839DB000F9297 /* NativeErrorConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = BC02E9090E1839DB000F9297 /* NativeErrorConstructor.h */; };
......@@ -737,9 +734,6 @@
A7E42C190E3938830065A544 /* JSStaticScopeObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSStaticScopeObject.cpp; sourceTree = "<group>"; };
A7F8690E0F9584A100558697 /* CachedCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedCall.h; sourceTree = "<group>"; };
A7F869EC0F95C2EC00558697 /* CallFrameClosure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallFrameClosure.h; sourceTree = "<group>"; };
A7F9935D0FD7325100A0B2D0 /* JSONObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSONObject.h; sourceTree = "<group>"; };
A7F9935E0FD7325100A0B2D0 /* JSONObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSONObject.cpp; sourceTree = "<group>"; };
A7F9949A0FD746A300A0B2D0 /* JSONObject.lut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JSONObject.lut.h; path = /Users/oliver/builds/Debug/DerivedSources/JavaScriptCore/JSONObject.lut.h; sourceTree = "<absolute>"; };
A8E894310CD0602400367179 /* JSCallbackObjectFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCallbackObjectFunctions.h; sourceTree = "<group>"; };
A8E894330CD0603F00367179 /* JSGlobalObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGlobalObject.h; sourceTree = "<group>"; };
BC02E9040E1839DB000F9297 /* ErrorConstructor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorConstructor.cpp; sourceTree = "<group>"; };
......@@ -1148,7 +1142,6 @@
children = (
BC18C5230E16FC8A00B34460 /* ArrayPrototype.lut.h */,
BCD203E70E1718F4002C7E82 /* DatePrototype.lut.h */,
A7F9949A0FD746A300A0B2D0 /* JSONObject.lut.h */,
BC18C5290E16FCC200B34460 /* MathObject.lut.h */,
BC2680E60E16D52300A06E92 /* NumberConstructor.lut.h */,
BCD202D50E170708002C7E82 /* RegExpConstructor.lut.h */,
......@@ -1469,8 +1462,6 @@
6507D2970E871E4A00D7D896 /* TypeInfo.h */,
F692A8850255597D01FF60F7 /* UString.cpp */,
F692A8860255597D01FF60F7 /* UString.h */,
A7F9935D0FD7325100A0B2D0 /* JSONObject.h */,
A7F9935E0FD7325100A0B2D0 /* JSONObject.cpp */,
);
path = runtime;
sourceTree = "<group>";
......@@ -1855,8 +1846,6 @@
960626960FB8EC02009798AB /* JITStubCall.h in Headers */,
0BDFFAE00FC6192900D69EF4 /* CrossThreadRefCounted.h in Headers */,
0BDFFAE10FC6193100D69EF4 /* OwnFastMallocPtr.h in Headers */,
A7F9935F0FD7325100A0B2D0 /* JSONObject.h in Headers */,
A7F9949B0FD746A300A0B2D0 /* JSONObject.lut.h in Headers */,
86ADD1450FDDEA980006EEC2 /* ARMv7Assembler.h in Headers */,
86ADD1460FDDEA980006EEC2 /* MacroAssemblerARMv7.h in Headers */,
);
......@@ -2220,7 +2209,6 @@
A7E2EA6C0FB460CF00601F06 /* LiteralParser.cpp in Sources */,
93052C340FB792190048FDC3 /* ParserArena.cpp in Sources */,
BCDD51EB0FB8DF74004A8BDC /* JITOpcodes.cpp in Sources */,
A7F993600FD7325100A0B2D0 /* JSONObject.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -89,7 +89,6 @@ namespace JSC {
#endif
static const HashTable* arrayTable(CallFrame* callFrame) { return callFrame->globalData().arrayTable; }
static const HashTable* dateTable(CallFrame* callFrame) { return callFrame->globalData().dateTable; }
static const HashTable* jsonTable(CallFrame* callFrame) { return callFrame->globalData().jsonTable; }
static const HashTable* mathTable(CallFrame* callFrame) { return callFrame->globalData().mathTable; }
static const HashTable* numberTable(CallFrame* callFrame) { return callFrame->globalData().numberTable; }
static const HashTable* regExpTable(CallFrame* callFrame) { return callFrame->globalData().regExpTable; }
......
......@@ -59,7 +59,6 @@
macro(test) \
macro(toExponential) \
macro(toFixed) \
macro(toJSON) \
macro(toLocaleString) \
macro(toPrecision) \
macro(toString) \
......
......@@ -60,7 +60,6 @@ using namespace WTF;
namespace JSC {
extern const HashTable arrayTable;
extern const HashTable jsonTable;
extern const HashTable dateTable;
extern const HashTable mathTable;
extern const HashTable numberTable;
......@@ -106,7 +105,6 @@ JSGlobalData::JSGlobalData(bool isShared, const VPtrSet& vptrSet)
, clientData(0)
, arrayTable(fastNew<HashTable>(JSC::arrayTable))
, dateTable(fastNew<HashTable>(JSC::dateTable))
, jsonTable(fastNew<HashTable>(JSC::jsonTable))
, mathTable(fastNew<HashTable>(JSC::mathTable))
, numberTable(fastNew<HashTable>(JSC::numberTable))
, regExpTable(fastNew<HashTable>(JSC::regExpTable))
......@@ -157,7 +155,6 @@ JSGlobalData::~JSGlobalData()
arrayTable->deleteTable();
dateTable->deleteTable();
jsonTable->deleteTable();
mathTable->deleteTable();
numberTable->deleteTable();
regExpTable->deleteTable();
......@@ -169,7 +166,6 @@ JSGlobalData::~JSGlobalData()
fastDelete(const_cast<HashTable*>(arrayTable));
fastDelete(const_cast<HashTable*>(dateTable));
fastDelete(const_cast<HashTable*>(jsonTable));
fastDelete(const_cast<HashTable*>(mathTable));
fastDelete(const_cast<HashTable*>(numberTable));
fastDelete(const_cast<HashTable*>(regExpTable));
......
......@@ -82,7 +82,6 @@ namespace JSC {
const HashTable* arrayTable;
const HashTable* dateTable;
const HashTable* jsonTable;
const HashTable* mathTable;
const HashTable* numberTable;
const HashTable* regExpTable;
......
......@@ -50,7 +50,6 @@
#include "JSFunction.h"
#include "JSGlobalObjectFunctions.h"
#include "JSLock.h"
#include "JSONObject.h"
#include "Interpreter.h"
#include "MathObject.h"
#include "NativeErrorConstructor.h"
......@@ -319,8 +318,7 @@ void JSGlobalObject::reset(JSValue prototype)
GlobalPropertyInfo(Identifier(exec, "Math"), new (exec) MathObject(exec, MathObject::createStructure(d()->objectPrototype)), DontEnum | DontDelete),
GlobalPropertyInfo(Identifier(exec, "NaN"), jsNaN(exec), DontEnum | DontDelete),
GlobalPropertyInfo(Identifier(exec, "Infinity"), jsNumber(exec, Inf), DontEnum | DontDelete),
GlobalPropertyInfo(Identifier(exec, "undefined"), jsUndefined(), DontEnum | DontDelete),
GlobalPropertyInfo(Identifier(exec, "JSON"), new (exec) JSONObject(JSONObject::createStructure(d()->objectPrototype)), DontEnum | DontDelete)
GlobalPropertyInfo(Identifier(exec, "undefined"), jsUndefined(), DontEnum | DontDelete)
};
addStaticGlobals(staticGlobals, sizeof(staticGlobals) / sizeof(GlobalPropertyInfo));
......
/*
* Copyright (C) 2009 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "JSONObject.h"
#include "ExceptionHelpers.h"
#include "JSArray.h"
#include "JSFunction.h"
#include "LiteralParser.h"
#include "Nodes.h"
#include "PropertyNameArray.h"
#include "ObjectPrototype.h"
#include "Operations.h"
#include <time.h>
#include <wtf/Assertions.h>
#include <wtf/MathExtras.h>
namespace JSC {
ASSERT_CLASS_FITS_IN_CELL(JSONObject);
static JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*, JSObject*, JSValue, const ArgList&);
}
#include "JSONObject.lut.h"
namespace JSC {
// ------------------------------ JSONObject --------------------------------
const ClassInfo JSONObject::info = { "JSON", 0, 0, ExecState::jsonTable };
/* Source for JSONObject.lut.h
@begin jsonTable
stringify JSONProtoFuncStringify DontEnum|Function 1
@end
*/
// ECMA 15.8
bool JSONObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
const HashEntry* entry = ExecState::jsonTable(exec)->entry(exec, propertyName);
if (!entry)
return JSObject::getOwnPropertySlot(exec, propertyName, slot);
ASSERT(entry->attributes() & Function);
setUpStaticFunctionSlot(exec, entry, this, propertyName, slot);
return true;
}
class Stringifier {
typedef UString StringBuilder;
// Basically the maximum nesting accepted by firefox.
// We can go much much higher without using all of the stack, but
// this provides us with a reasonable leeway as to how deep the
// stack is when we start.
enum { MaxObjectDepth = 1792 };
public:
Stringifier(ExecState* exec, JSValue replacer, const UString& gap)
: m_exec(exec)
, m_replacer(replacer)
, m_gap(gap)
, m_arrayReplacerPropertyNames(exec)
, m_usingArrayReplacer(false)
, m_replacerCallType(CallTypeNone)
{
if (m_replacer.isObject()) {
if (asObject(m_replacer)->inherits(&JSArray::info)) {
JSObject* array = asObject(m_replacer);
bool isRealJSArray = isJSArray(&m_exec->globalData(), m_replacer);
unsigned length = array->get(m_exec, m_exec->globalData().propertyNames->length).toUInt32(m_exec);
for (unsigned i = 0; i < length; i++) {
JSValue name;
if (isRealJSArray && asArray(array)->canGetIndex(i))
name = asArray(array)->getIndex(i);
else {
name = array->get(m_exec, i);
if (exec->hadException())
break;
}
if (!name.isString())
continue;
UString propertyName = name.toString(m_exec);
if (exec->hadException())
return;
m_arrayReplacerPropertyNames.add(Identifier(m_exec, propertyName));
}
m_usingArrayReplacer = true;
} else
m_replacerCallType = asObject(m_replacer)->getCallData(m_replacerCallData);
}
}
JSValue stringify(JSValue value)
{
JSObject* obj = constructEmptyObject(m_exec);
if (m_exec->hadException())
return jsNull();
Identifier emptyIdent(m_exec, "");
obj->putDirect(emptyIdent, value);
StringKeyGenerator key(emptyIdent);
value = toJSONValue(key, value);
StringBuilder builder;
if (!stringify(builder, obj, key, value))
return jsUndefined();
return (m_exec->hadException())? m_exec->exception() : jsString(m_exec, builder);
}
private:
void appendString(StringBuilder& builder, const UString& value)
{
int length = value.size();
const UChar* data = value.data();
builder.reserveCapacity(builder.size() + length + 2 + 8); // +2 for "'s +8 for a buffer
builder.append('\"');
int index = 0;
while (index < length) {
int start = index;
while (index < length && (data[index] > 0x1f && data[index] != '"' && data[index] != '\\'))
index++;
builder.append(data + start, index - start);
if (index < length) {
switch(data[index]){
case '\t': {
UChar tab[] = {'\\','t'};
builder.append(tab, 2);
break;
}
case '\r': {
UChar cr[] = {'\\','r'};
builder.append(cr, 2);
break;
}
case '\n': {
UChar nl[] = {'\\','n'};
builder.append(nl, 2);
break;
}
case '\f': {
UChar tab[] = {'\\','f'};
builder.append(tab, 2);
break;
}
case '\b': {
UChar bs[] = {'\\','b'};
builder.append(bs, 2);
break;
}
case '\"': {
UChar quote[] = {'\\','"'};
builder.append(quote, 2);
break;
}
case '\\': {
UChar slash[] = {'\\', '\\'};
builder.append(slash, 2);
break;
}
default:
int ch = (int)data[index];
static const char* hexStr = "0123456789abcdef";
UChar oct[] = {'\\', 'u', hexStr[(ch >> 12) & 15], hexStr[(ch >> 8) & 15], hexStr[(ch >> 4) & 15], hexStr[(ch >> 0) & 15]};
builder.append(oct, sizeof(oct) / sizeof(UChar));
break;
}
index++;
}
}
builder.append('\"');
}
class StringKeyGenerator {
public:
StringKeyGenerator(const Identifier& property)
: m_property(property)
{
}
JSValue getKey(ExecState* exec) { if (!m_key) m_key = jsString(exec, m_property.ustring()); return m_key; }
private:
Identifier m_property;
JSValue m_key;
};
class IntKeyGenerator {
public:
IntKeyGenerator(uint32_t property)
: m_property(property)
{
}
JSValue getKey(ExecState* exec) { if (!m_key) m_key = jsNumber(exec, m_property); return m_key; }
private:
uint32_t m_property;
JSValue m_key;
};
template <typename KeyGenerator> JSValue toJSONValue(KeyGenerator& jsKey, JSValue jsValue)
{
if (!jsValue.isObject() || !asObject(jsValue)->hasProperty(m_exec, m_exec->globalData().propertyNames->toJSON))
return jsValue;
JSValue jsToJSON = asObject(jsValue)->get(m_exec, m_exec->globalData().propertyNames->toJSON);
if (!jsToJSON.isObject())
return jsValue;
JSObject* object = asObject(jsToJSON);
CallData callData;
CallType callType = object->getCallData(callData);
if (callType == CallTypeNone)
return jsValue;
JSValue list[] = { jsKey.getKey(m_exec) };
ArgList args(list, sizeof(list) / sizeof(JSValue));
return call(m_exec, object, callType, callData, jsValue, args);
}
bool stringifyArray(StringBuilder& builder, JSArray* array)
{
if (m_objectStack.contains(array)) {
throwError(m_exec, TypeError);
return false;
}
if (m_objectStack.size() > MaxObjectDepth) {
m_exec->setException(createStackOverflowError(m_exec));
return false;
}
m_objectStack.add(array);
UString stepBack = m_indent;
m_indent.append(m_gap);
Vector<StringBuilder, 16> partial;
unsigned len = array->get(m_exec, m_exec->globalData().propertyNames->length).toUInt32(m_exec);
bool isRealJSArray = isJSArray(&m_exec->globalData(), array);
for (unsigned i = 0; i < len; i++) {
JSValue value;
if (isRealJSArray && array->canGetIndex(i))
value = array->getIndex(i);
else {
value = array->get(m_exec, i);
if (m_exec->hadException())
return false;
}
StringBuilder result;
IntKeyGenerator key(i);
value = toJSONValue(key, value);
if (partial.size() && m_gap.isEmpty())
partial.last().append(',');
if (!stringify(result, array, key, value) && !m_exec->hadException())
result.append("null");
else if (m_exec->hadException())
return false;
partial.append(result);
}
if (partial.isEmpty())
builder.append("[]");
else {
if (m_gap.isEmpty()) {
builder.append('[');
for (unsigned i = 0; i < partial.size(); i++)
builder.append(partial[i]);
builder.append(']');
} else {
UString separator(",\n");
separator.append(m_indent);
builder.append("[\n");
builder.append(m_indent);
for (unsigned i = 0; i < partial.size(); i++) {
builder.append(partial[i]);
if (i < partial.size() - 1)
builder.append(separator);
}
builder.append('\n');
builder.append(stepBack);
builder.append(']');
}
}
m_indent = stepBack;
m_objectStack.remove(array);
return true;
}
bool stringifyObject(StringBuilder& builder, JSObject* object)
{
if (m_objectStack.contains(object)) {
throwError(m_exec, TypeError);
return false;
}
if (m_objectStack.size() > MaxObjectDepth) {
m_exec->setException(createStackOverflowError(m_exec));
return false;
}
m_objectStack.add(object);
UString stepBack = m_indent;
m_indent.append(m_gap);
Vector<StringBuilder, 16> partial;
PropertyNameArray objectPropertyNames(m_exec);
PropertyNameArray& sourcePropertyNames(m_arrayReplacerPropertyNames);
if (!m_usingArrayReplacer) {
object->getPropertyNames(m_exec, objectPropertyNames);
sourcePropertyNames = objectPropertyNames;
}
PropertyNameArray::const_iterator propEnd = sourcePropertyNames.end();
for (PropertyNameArray::const_iterator propIter = sourcePropertyNames.begin(); propIter != propEnd; propIter++) {
if (!object->hasOwnProperty(m_exec, *propIter))
continue;
JSValue value = object->get(m_exec, *propIter);
if (value.isUndefined())
continue;
StringBuilder result;
appendString(result,propIter->ustring());
result.append(':');
if (!m_gap.isEmpty())
result.append(' ');
StringKeyGenerator key(*propIter);
value = toJSONValue(key, value);
if (value.isUndefined())
continue;
stringify(result, object, key, value);
partial.append(result);
}
if (partial.isEmpty())
builder.append("{}");
else {
if (m_gap.isEmpty()) {
builder.append('{');
for (unsigned i = 0; i < partial.size(); i++) {
if (i > 0)
builder.append(',');
builder.append(partial[i]);