JavaScriptCore/runtime/JSONObject.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2009 Apple Inc. All rights reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  * 1. Redistributions of source code must retain the above copyright
       
     8  *    notice, this list of conditions and the following disclaimer.
       
     9  * 2. Redistributions in binary form must reproduce the above copyright
       
    10  *    notice, this list of conditions and the following disclaimer in the
       
    11  *    documentation and/or other materials provided with the distribution.
       
    12  *
       
    13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
       
    14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
       
    17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
       
    21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
       
    24  */
       
    25 
       
    26 #include "config.h"
       
    27 #include "JSONObject.h"
       
    28 
       
    29 #include "BooleanObject.h"
       
    30 #include "Error.h"
       
    31 #include "ExceptionHelpers.h"
       
    32 #include "JSArray.h"
       
    33 #include "JSGlobalObject.h"
       
    34 #include "LiteralParser.h"
       
    35 #include "Lookup.h"
       
    36 #include "PropertyNameArray.h"
       
    37 #include "StringBuilder.h"
       
    38 #include <wtf/MathExtras.h>
       
    39 
       
    40 namespace JSC {
       
    41 
       
    42 ASSERT_CLASS_FITS_IN_CELL(JSONObject);
       
    43 
       
    44 static EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*);
       
    45 static EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*);
       
    46 
       
    47 }
       
    48 
       
    49 #include "JSONObject.lut.h"
       
    50 
       
    51 namespace JSC {
       
    52 
       
    53 JSONObject::JSONObject(JSGlobalObject* globalObject, NonNullPassRefPtr<Structure> structure)
       
    54     : JSObjectWithGlobalObject(globalObject, structure)
       
    55 {
       
    56 }
       
    57 
       
    58 // PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked.
       
    59 class PropertyNameForFunctionCall {
       
    60 public:
       
    61     PropertyNameForFunctionCall(const Identifier&);
       
    62     PropertyNameForFunctionCall(unsigned);
       
    63 
       
    64     JSValue value(ExecState*) const;
       
    65 
       
    66 private:
       
    67     const Identifier* m_identifier;
       
    68     unsigned m_number;
       
    69     mutable JSValue m_value;
       
    70 };
       
    71 
       
    72 class Stringifier : public Noncopyable {
       
    73 public:
       
    74     Stringifier(ExecState*, JSValue replacer, JSValue space);
       
    75     ~Stringifier();
       
    76     JSValue stringify(JSValue);
       
    77 
       
    78     void markAggregate(MarkStack&);
       
    79 
       
    80 private:
       
    81     class Holder {
       
    82     public:
       
    83         Holder(JSObject*);
       
    84 
       
    85         JSObject* object() const { return m_object; }
       
    86 
       
    87         bool appendNextProperty(Stringifier&, StringBuilder&);
       
    88 
       
    89     private:
       
    90         JSObject* const m_object;
       
    91         const bool m_isArray;
       
    92         bool m_isJSArray;
       
    93         unsigned m_index;
       
    94         unsigned m_size;
       
    95         RefPtr<PropertyNameArrayData> m_propertyNames;
       
    96     };
       
    97 
       
    98     friend class Holder;
       
    99 
       
   100     static void appendQuotedString(StringBuilder&, const UString&);
       
   101 
       
   102     JSValue toJSON(JSValue, const PropertyNameForFunctionCall&);
       
   103 
       
   104     enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedValue };
       
   105     StringifyResult appendStringifiedValue(StringBuilder&, JSValue, JSObject* holder, const PropertyNameForFunctionCall&);
       
   106 
       
   107     bool willIndent() const;
       
   108     void indent();
       
   109     void unindent();
       
   110     void startNewLine(StringBuilder&) const;
       
   111 
       
   112     Stringifier* const m_nextStringifierToMark;
       
   113     ExecState* const m_exec;
       
   114     const JSValue m_replacer;
       
   115     bool m_usingArrayReplacer;
       
   116     PropertyNameArray m_arrayReplacerPropertyNames;
       
   117     CallType m_replacerCallType;
       
   118     CallData m_replacerCallData;
       
   119     const UString m_gap;
       
   120 
       
   121     HashSet<JSObject*> m_holderCycleDetector;
       
   122     Vector<Holder, 16> m_holderStack;
       
   123     UString m_repeatedGap;
       
   124     UString m_indent;
       
   125 };
       
   126 
       
   127 // ------------------------------ helper functions --------------------------------
       
   128 
       
   129 static inline JSValue unwrapBoxedPrimitive(ExecState* exec, JSValue value)
       
   130 {
       
   131     if (!value.isObject())
       
   132         return value;
       
   133     JSObject* object = asObject(value);
       
   134     if (object->inherits(&NumberObject::info))
       
   135         return jsNumber(exec, object->toNumber(exec));
       
   136     if (object->inherits(&StringObject::info))
       
   137         return jsString(exec, object->toString(exec));
       
   138     if (object->inherits(&BooleanObject::info))
       
   139         return object->toPrimitive(exec);
       
   140     return value;
       
   141 }
       
   142 
       
   143 static inline UString gap(ExecState* exec, JSValue space)
       
   144 {
       
   145     const unsigned maxGapLength = 10;
       
   146     space = unwrapBoxedPrimitive(exec, space);
       
   147 
       
   148     // If the space value is a number, create a gap string with that number of spaces.
       
   149     double spaceCount;
       
   150     if (space.getNumber(spaceCount)) {
       
   151         int count;
       
   152         if (spaceCount > maxGapLength)
       
   153             count = maxGapLength;
       
   154         else if (!(spaceCount > 0))
       
   155             count = 0;
       
   156         else
       
   157             count = static_cast<int>(spaceCount);
       
   158         UChar spaces[maxGapLength];
       
   159         for (int i = 0; i < count; ++i)
       
   160             spaces[i] = ' ';
       
   161         return UString(spaces, count);
       
   162     }
       
   163 
       
   164     // If the space value is a string, use it as the gap string, otherwise use no gap string.
       
   165     UString spaces = space.getString(exec);
       
   166     if (spaces.size() > maxGapLength) {
       
   167         spaces = spaces.substr(0, maxGapLength);
       
   168     }
       
   169     return spaces;
       
   170 }
       
   171 
       
   172 // ------------------------------ PropertyNameForFunctionCall --------------------------------
       
   173 
       
   174 inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(const Identifier& identifier)
       
   175     : m_identifier(&identifier)
       
   176 {
       
   177 }
       
   178 
       
   179 inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number)
       
   180     : m_identifier(0)
       
   181     , m_number(number)
       
   182 {
       
   183 }
       
   184 
       
   185 JSValue PropertyNameForFunctionCall::value(ExecState* exec) const
       
   186 {
       
   187     if (!m_value) {
       
   188         if (m_identifier)
       
   189             m_value = jsString(exec, m_identifier->ustring());
       
   190         else
       
   191             m_value = jsNumber(exec, m_number);
       
   192     }
       
   193     return m_value;
       
   194 }
       
   195 
       
   196 // ------------------------------ Stringifier --------------------------------
       
   197 
       
   198 Stringifier::Stringifier(ExecState* exec, JSValue replacer, JSValue space)
       
   199     : m_nextStringifierToMark(exec->globalData().firstStringifierToMark)
       
   200     , m_exec(exec)
       
   201     , m_replacer(replacer)
       
   202     , m_usingArrayReplacer(false)
       
   203     , m_arrayReplacerPropertyNames(exec)
       
   204     , m_replacerCallType(CallTypeNone)
       
   205     , m_gap(gap(exec, space))
       
   206 {
       
   207     exec->globalData().firstStringifierToMark = this;
       
   208 
       
   209     if (!m_replacer.isObject())
       
   210         return;
       
   211 
       
   212     if (asObject(m_replacer)->inherits(&JSArray::info)) {
       
   213         m_usingArrayReplacer = true;
       
   214         JSObject* array = asObject(m_replacer);
       
   215         unsigned length = array->get(exec, exec->globalData().propertyNames->length).toUInt32(exec);
       
   216         for (unsigned i = 0; i < length; ++i) {
       
   217             JSValue name = array->get(exec, i);
       
   218             if (exec->hadException())
       
   219                 break;
       
   220 
       
   221             UString propertyName;
       
   222             if (name.getString(exec, propertyName)) {
       
   223                 m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName));
       
   224                 continue;
       
   225             }
       
   226 
       
   227             double value = 0;
       
   228             if (name.getNumber(value)) {
       
   229                 m_arrayReplacerPropertyNames.add(Identifier::from(exec, value));
       
   230                 continue;
       
   231             }
       
   232 
       
   233             if (name.isObject()) {
       
   234                 if (!asObject(name)->inherits(&NumberObject::info) && !asObject(name)->inherits(&StringObject::info))
       
   235                     continue;
       
   236                 propertyName = name.toString(exec);
       
   237                 if (exec->hadException())
       
   238                     break;
       
   239                 m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName));
       
   240             }
       
   241         }
       
   242         return;
       
   243     }
       
   244 
       
   245     m_replacerCallType = asObject(m_replacer)->getCallData(m_replacerCallData);
       
   246 }
       
   247 
       
   248 Stringifier::~Stringifier()
       
   249 {
       
   250     ASSERT(m_exec->globalData().firstStringifierToMark == this);
       
   251     m_exec->globalData().firstStringifierToMark = m_nextStringifierToMark;
       
   252 }
       
   253 
       
   254 void Stringifier::markAggregate(MarkStack& markStack)
       
   255 {
       
   256     for (Stringifier* stringifier = this; stringifier; stringifier = stringifier->m_nextStringifierToMark) {
       
   257         size_t size = m_holderStack.size();
       
   258         for (size_t i = 0; i < size; ++i)
       
   259             markStack.append(m_holderStack[i].object());
       
   260     }
       
   261 }
       
   262 
       
   263 JSValue Stringifier::stringify(JSValue value)
       
   264 {
       
   265     JSObject* object = constructEmptyObject(m_exec);
       
   266     if (m_exec->hadException())
       
   267         return jsNull();
       
   268 
       
   269     PropertyNameForFunctionCall emptyPropertyName(m_exec->globalData().propertyNames->emptyIdentifier);
       
   270     object->putDirect(m_exec->globalData().propertyNames->emptyIdentifier, value);
       
   271 
       
   272     StringBuilder result;
       
   273     if (appendStringifiedValue(result, value, object, emptyPropertyName) != StringifySucceeded)
       
   274         return jsUndefined();
       
   275     if (m_exec->hadException())
       
   276         return jsNull();
       
   277 
       
   278     return jsString(m_exec, result.build());
       
   279 }
       
   280 
       
   281 void Stringifier::appendQuotedString(StringBuilder& builder, const UString& value)
       
   282 {
       
   283     int length = value.size();
       
   284 
       
   285     // String length plus 2 for quote marks plus 8 so we can accomodate a few escaped characters.
       
   286     builder.reserveCapacity(builder.size() + length + 2 + 8);
       
   287 
       
   288     builder.append('"');
       
   289 
       
   290     const UChar* data = value.data();
       
   291     for (int i = 0; i < length; ++i) {
       
   292         int start = i;
       
   293         while (i < length && (data[i] > 0x1F && data[i] != '"' && data[i] != '\\'))
       
   294             ++i;
       
   295         builder.append(data + start, i - start);
       
   296         if (i >= length)
       
   297             break;
       
   298         switch (data[i]) {
       
   299             case '\t':
       
   300                 builder.append('\\');
       
   301                 builder.append('t');
       
   302                 break;
       
   303             case '\r':
       
   304                 builder.append('\\');
       
   305                 builder.append('r');
       
   306                 break;
       
   307             case '\n':
       
   308                 builder.append('\\');
       
   309                 builder.append('n');
       
   310                 break;
       
   311             case '\f':
       
   312                 builder.append('\\');
       
   313                 builder.append('f');
       
   314                 break;
       
   315             case '\b':
       
   316                 builder.append('\\');
       
   317                 builder.append('b');
       
   318                 break;
       
   319             case '"':
       
   320                 builder.append('\\');
       
   321                 builder.append('"');
       
   322                 break;
       
   323             case '\\':
       
   324                 builder.append('\\');
       
   325                 builder.append('\\');
       
   326                 break;
       
   327             default:
       
   328                 static const char hexDigits[] = "0123456789abcdef";
       
   329                 UChar ch = data[i];
       
   330                 UChar hex[] = { '\\', 'u', hexDigits[(ch >> 12) & 0xF], hexDigits[(ch >> 8) & 0xF], hexDigits[(ch >> 4) & 0xF], hexDigits[ch & 0xF] };
       
   331                 builder.append(hex, sizeof(hex) / sizeof(UChar));
       
   332                 break;
       
   333         }
       
   334     }
       
   335 
       
   336     builder.append('"');
       
   337 }
       
   338 
       
   339 inline JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionCall& propertyName)
       
   340 {
       
   341     ASSERT(!m_exec->hadException());
       
   342     if (!value.isObject() || !asObject(value)->hasProperty(m_exec, m_exec->globalData().propertyNames->toJSON))
       
   343         return value;
       
   344 
       
   345     JSValue toJSONFunction = asObject(value)->get(m_exec, m_exec->globalData().propertyNames->toJSON);
       
   346     if (m_exec->hadException())
       
   347         return jsNull();
       
   348 
       
   349     if (!toJSONFunction.isObject())
       
   350         return value;
       
   351 
       
   352     JSObject* object = asObject(toJSONFunction);
       
   353     CallData callData;
       
   354     CallType callType = object->getCallData(callData);
       
   355     if (callType == CallTypeNone)
       
   356         return value;
       
   357 
       
   358     JSValue list[] = { propertyName.value(m_exec) };
       
   359     ArgList args(list, sizeof(list) / sizeof(JSValue));
       
   360     return call(m_exec, object, callType, callData, value, args);
       
   361 }
       
   362 
       
   363 Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName)
       
   364 {
       
   365     // Call the toJSON function.
       
   366     value = toJSON(value, propertyName);
       
   367     if (m_exec->hadException())
       
   368         return StringifyFailed;
       
   369 
       
   370     // Call the replacer function.
       
   371     if (m_replacerCallType != CallTypeNone) {
       
   372         JSValue list[] = { propertyName.value(m_exec), value };
       
   373         ArgList args(list, sizeof(list) / sizeof(JSValue));
       
   374         value = call(m_exec, m_replacer, m_replacerCallType, m_replacerCallData, holder, args);
       
   375         if (m_exec->hadException())
       
   376             return StringifyFailed;
       
   377     }
       
   378 
       
   379     if (value.isUndefined() && !holder->inherits(&JSArray::info))
       
   380         return StringifyFailedDueToUndefinedValue;
       
   381 
       
   382     if (value.isNull()) {
       
   383         builder.append("null");
       
   384         return StringifySucceeded;
       
   385     }
       
   386 
       
   387     value = unwrapBoxedPrimitive(m_exec, value);
       
   388 
       
   389     if (m_exec->hadException())
       
   390         return StringifyFailed;
       
   391 
       
   392     if (value.isBoolean()) {
       
   393         builder.append(value.getBoolean() ? "true" : "false");
       
   394         return StringifySucceeded;
       
   395     }
       
   396 
       
   397     UString stringValue;
       
   398     if (value.getString(m_exec, stringValue)) {
       
   399         appendQuotedString(builder, stringValue);
       
   400         return StringifySucceeded;
       
   401     }
       
   402 
       
   403     double numericValue;
       
   404     if (value.getNumber(numericValue)) {
       
   405         if (!isfinite(numericValue))
       
   406             builder.append("null");
       
   407         else
       
   408             builder.append(UString::from(numericValue));
       
   409         return StringifySucceeded;
       
   410     }
       
   411 
       
   412     if (!value.isObject())
       
   413         return StringifyFailed;
       
   414 
       
   415     JSObject* object = asObject(value);
       
   416 
       
   417     CallData callData;
       
   418     if (object->getCallData(callData) != CallTypeNone) {
       
   419         if (holder->inherits(&JSArray::info)) {
       
   420             builder.append("null");
       
   421             return StringifySucceeded;
       
   422         }
       
   423         return StringifyFailedDueToUndefinedValue;
       
   424     }
       
   425 
       
   426     // Handle cycle detection, and put the holder on the stack.
       
   427     if (!m_holderCycleDetector.add(object).second) {
       
   428         throwError(m_exec, createTypeError(m_exec, "JSON.stringify cannot serialize cyclic structures."));
       
   429         return StringifyFailed;
       
   430     }
       
   431     bool holderStackWasEmpty = m_holderStack.isEmpty();
       
   432     m_holderStack.append(object);
       
   433     if (!holderStackWasEmpty)
       
   434         return StringifySucceeded;
       
   435 
       
   436     // If this is the outermost call, then loop to handle everything on the holder stack.
       
   437     TimeoutChecker localTimeoutChecker(m_exec->globalData().timeoutChecker);
       
   438     localTimeoutChecker.reset();
       
   439     unsigned tickCount = localTimeoutChecker.ticksUntilNextCheck();
       
   440     do {
       
   441         while (m_holderStack.last().appendNextProperty(*this, builder)) {
       
   442             if (m_exec->hadException())
       
   443                 return StringifyFailed;
       
   444             if (!--tickCount) {
       
   445                 if (localTimeoutChecker.didTimeOut(m_exec)) {
       
   446                     throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData()));
       
   447                     return StringifyFailed;
       
   448                 }
       
   449                 tickCount = localTimeoutChecker.ticksUntilNextCheck();
       
   450             }
       
   451         }
       
   452         m_holderCycleDetector.remove(m_holderStack.last().object());
       
   453         m_holderStack.removeLast();
       
   454     } while (!m_holderStack.isEmpty());
       
   455     return StringifySucceeded;
       
   456 }
       
   457 
       
   458 inline bool Stringifier::willIndent() const
       
   459 {
       
   460     return !m_gap.isEmpty();
       
   461 }
       
   462 
       
   463 inline void Stringifier::indent()
       
   464 {
       
   465     // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent.
       
   466     unsigned newSize = m_indent.size() + m_gap.size();
       
   467     if (newSize > m_repeatedGap.size())
       
   468         m_repeatedGap = makeString(m_repeatedGap, m_gap);
       
   469     ASSERT(newSize <= m_repeatedGap.size());
       
   470     m_indent = m_repeatedGap.substr(0, newSize);
       
   471 }
       
   472 
       
   473 inline void Stringifier::unindent()
       
   474 {
       
   475     ASSERT(m_indent.size() >= m_gap.size());
       
   476     m_indent = m_repeatedGap.substr(0, m_indent.size() - m_gap.size());
       
   477 }
       
   478 
       
   479 inline void Stringifier::startNewLine(StringBuilder& builder) const
       
   480 {
       
   481     if (m_gap.isEmpty())
       
   482         return;
       
   483     builder.append('\n');
       
   484     builder.append(m_indent);
       
   485 }
       
   486 
       
   487 inline Stringifier::Holder::Holder(JSObject* object)
       
   488     : m_object(object)
       
   489     , m_isArray(object->inherits(&JSArray::info))
       
   490     , m_index(0)
       
   491 {
       
   492 }
       
   493 
       
   494 bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBuilder& builder)
       
   495 {
       
   496     ASSERT(m_index <= m_size);
       
   497 
       
   498     ExecState* exec = stringifier.m_exec;
       
   499 
       
   500     // First time through, initialize.
       
   501     if (!m_index) {
       
   502         if (m_isArray) {
       
   503             m_isJSArray = isJSArray(&exec->globalData(), m_object);
       
   504             m_size = m_object->get(exec, exec->globalData().propertyNames->length).toUInt32(exec);
       
   505             builder.append('[');
       
   506         } else {
       
   507             if (stringifier.m_usingArrayReplacer)
       
   508                 m_propertyNames = stringifier.m_arrayReplacerPropertyNames.data();
       
   509             else {
       
   510                 PropertyNameArray objectPropertyNames(exec);
       
   511                 m_object->getOwnPropertyNames(exec, objectPropertyNames);
       
   512                 m_propertyNames = objectPropertyNames.releaseData();
       
   513             }
       
   514             m_size = m_propertyNames->propertyNameVector().size();
       
   515             builder.append('{');
       
   516         }
       
   517         stringifier.indent();
       
   518     }
       
   519 
       
   520     // Last time through, finish up and return false.
       
   521     if (m_index == m_size) {
       
   522         stringifier.unindent();
       
   523         if (m_size && builder[builder.size() - 1] != '{')
       
   524             stringifier.startNewLine(builder);
       
   525         builder.append(m_isArray ? ']' : '}');
       
   526         return false;
       
   527     }
       
   528 
       
   529     // Handle a single element of the array or object.
       
   530     unsigned index = m_index++;
       
   531     unsigned rollBackPoint = 0;
       
   532     StringifyResult stringifyResult;
       
   533     if (m_isArray) {
       
   534         // Get the value.
       
   535         JSValue value;
       
   536         if (m_isJSArray && asArray(m_object)->canGetIndex(index))
       
   537             value = asArray(m_object)->getIndex(index);
       
   538         else {
       
   539             PropertySlot slot(m_object);
       
   540             if (!m_object->getOwnPropertySlot(exec, index, slot))
       
   541                 slot.setUndefined();
       
   542             if (exec->hadException())
       
   543                 return false;
       
   544             value = slot.getValue(exec, index);
       
   545         }
       
   546 
       
   547         // Append the separator string.
       
   548         if (index)
       
   549             builder.append(',');
       
   550         stringifier.startNewLine(builder);
       
   551 
       
   552         // Append the stringified value.
       
   553         stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object, index);
       
   554     } else {
       
   555         // Get the value.
       
   556         PropertySlot slot(m_object);
       
   557         Identifier& propertyName = m_propertyNames->propertyNameVector()[index];
       
   558         if (!m_object->getOwnPropertySlot(exec, propertyName, slot))
       
   559             return true;
       
   560         JSValue value = slot.getValue(exec, propertyName);
       
   561         if (exec->hadException())
       
   562             return false;
       
   563 
       
   564         rollBackPoint = builder.size();
       
   565 
       
   566         // Append the separator string.
       
   567         if (builder[rollBackPoint - 1] != '{')
       
   568             builder.append(',');
       
   569         stringifier.startNewLine(builder);
       
   570 
       
   571         // Append the property name.
       
   572         appendQuotedString(builder, propertyName.ustring());
       
   573         builder.append(':');
       
   574         if (stringifier.willIndent())
       
   575             builder.append(' ');
       
   576 
       
   577         // Append the stringified value.
       
   578         stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object, propertyName);
       
   579     }
       
   580 
       
   581     // From this point on, no access to the this pointer or to any members, because the
       
   582     // Holder object may have moved if the call to stringify pushed a new Holder onto
       
   583     // m_holderStack.
       
   584 
       
   585     switch (stringifyResult) {
       
   586         case StringifyFailed:
       
   587             builder.append("null");
       
   588             break;
       
   589         case StringifySucceeded:
       
   590             break;
       
   591         case StringifyFailedDueToUndefinedValue:
       
   592             // This only occurs when get an undefined value for an object property.
       
   593             // In this case we don't want the separator and property name that we
       
   594             // already appended, so roll back.
       
   595             builder.resize(rollBackPoint);
       
   596             break;
       
   597     }
       
   598 
       
   599     return true;
       
   600 }
       
   601 
       
   602 // ------------------------------ JSONObject --------------------------------
       
   603 
       
   604 const ClassInfo JSONObject::info = { "JSON", 0, 0, ExecState::jsonTable };
       
   605 
       
   606 /* Source for JSONObject.lut.h
       
   607 @begin jsonTable
       
   608   parse         JSONProtoFuncParse             DontEnum|Function 1
       
   609   stringify     JSONProtoFuncStringify         DontEnum|Function 1
       
   610 @end
       
   611 */
       
   612 
       
   613 // ECMA 15.8
       
   614 
       
   615 bool JSONObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
       
   616 {
       
   617     return getStaticFunctionSlot<JSObject>(exec, ExecState::jsonTable(exec), this, propertyName, slot);
       
   618 }
       
   619 
       
   620 bool JSONObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
       
   621 {
       
   622     return getStaticFunctionDescriptor<JSObject>(exec, ExecState::jsonTable(exec), this, propertyName, descriptor);
       
   623 }
       
   624 
       
   625 void JSONObject::markStringifiers(MarkStack& markStack, Stringifier* stringifier)
       
   626 {
       
   627     stringifier->markAggregate(markStack);
       
   628 }
       
   629 
       
   630 class Walker {
       
   631 public:
       
   632     Walker(ExecState* exec, JSObject* function, CallType callType, CallData callData)
       
   633         : m_exec(exec)
       
   634         , m_function(function)
       
   635         , m_callType(callType)
       
   636         , m_callData(callData)
       
   637     {
       
   638     }
       
   639     JSValue walk(JSValue unfiltered);
       
   640 private:
       
   641     JSValue callReviver(JSObject* thisObj, JSValue property, JSValue unfiltered)
       
   642     {
       
   643         JSValue args[] = { property, unfiltered };
       
   644         ArgList argList(args, 2);
       
   645         return call(m_exec, m_function, m_callType, m_callData, thisObj, argList);
       
   646     }
       
   647 
       
   648     friend class Holder;
       
   649 
       
   650     ExecState* m_exec;
       
   651     JSObject* m_function;
       
   652     CallType m_callType;
       
   653     CallData m_callData;
       
   654 };
       
   655 
       
   656 // We clamp recursion well beyond anything reasonable, but we also have a timeout check
       
   657 // to guard against "infinite" execution by inserting arbitrarily large objects.
       
   658 static const unsigned maximumFilterRecursion = 40000;
       
   659 enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember, 
       
   660                                  ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember };
       
   661 NEVER_INLINE JSValue Walker::walk(JSValue unfiltered)
       
   662 {
       
   663     Vector<PropertyNameArray, 16> propertyStack;
       
   664     Vector<uint32_t, 16> indexStack;
       
   665     Vector<JSObject*, 16> objectStack;
       
   666     Vector<JSArray*, 16> arrayStack;
       
   667     
       
   668     Vector<WalkerState, 16> stateStack;
       
   669     WalkerState state = StateUnknown;
       
   670     JSValue inValue = unfiltered;
       
   671     JSValue outValue = jsNull();
       
   672     
       
   673     TimeoutChecker localTimeoutChecker(m_exec->globalData().timeoutChecker);
       
   674     localTimeoutChecker.reset();
       
   675     unsigned tickCount = localTimeoutChecker.ticksUntilNextCheck();
       
   676     while (1) {
       
   677         switch (state) {
       
   678             arrayStartState:
       
   679             case ArrayStartState: {
       
   680                 ASSERT(inValue.isObject());
       
   681                 ASSERT(isJSArray(&m_exec->globalData(), asObject(inValue)) || asObject(inValue)->inherits(&JSArray::info));
       
   682                 if (objectStack.size() + arrayStack.size() > maximumFilterRecursion)
       
   683                     return throwError(m_exec, createStackOverflowError(m_exec));
       
   684 
       
   685                 JSArray* array = asArray(inValue);
       
   686                 arrayStack.append(array);
       
   687                 indexStack.append(0);
       
   688                 // fallthrough
       
   689             }
       
   690             arrayStartVisitMember:
       
   691             case ArrayStartVisitMember: {
       
   692                 if (!--tickCount) {
       
   693                     if (localTimeoutChecker.didTimeOut(m_exec))
       
   694                         return throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData()));
       
   695                     tickCount = localTimeoutChecker.ticksUntilNextCheck();
       
   696                 }
       
   697 
       
   698                 JSArray* array = arrayStack.last();
       
   699                 uint32_t index = indexStack.last();
       
   700                 if (index == array->length()) {
       
   701                     outValue = array;
       
   702                     arrayStack.removeLast();
       
   703                     indexStack.removeLast();
       
   704                     break;
       
   705                 }
       
   706                 if (isJSArray(&m_exec->globalData(), array) && array->canGetIndex(index))
       
   707                     inValue = array->getIndex(index);
       
   708                 else {
       
   709                     PropertySlot slot;
       
   710                     if (array->getOwnPropertySlot(m_exec, index, slot))
       
   711                         inValue = slot.getValue(m_exec, index);
       
   712                     else
       
   713                         inValue = jsUndefined();
       
   714                 }
       
   715                     
       
   716                 if (inValue.isObject()) {
       
   717                     stateStack.append(ArrayEndVisitMember);
       
   718                     goto stateUnknown;
       
   719                 } else
       
   720                     outValue = inValue;
       
   721                 // fallthrough
       
   722             }
       
   723             case ArrayEndVisitMember: {
       
   724                 JSArray* array = arrayStack.last();
       
   725                 JSValue filteredValue = callReviver(array, jsString(m_exec, UString::from(indexStack.last())), outValue);
       
   726                 if (filteredValue.isUndefined())
       
   727                     array->deleteProperty(m_exec, indexStack.last());
       
   728                 else {
       
   729                     if (isJSArray(&m_exec->globalData(), array) && array->canSetIndex(indexStack.last()))
       
   730                         array->setIndex(indexStack.last(), filteredValue);
       
   731                     else
       
   732                         array->put(m_exec, indexStack.last(), filteredValue);
       
   733                 }
       
   734                 if (m_exec->hadException())
       
   735                     return jsNull();
       
   736                 indexStack.last()++;
       
   737                 goto arrayStartVisitMember;
       
   738             }
       
   739             objectStartState:
       
   740             case ObjectStartState: {
       
   741                 ASSERT(inValue.isObject());
       
   742                 ASSERT(!isJSArray(&m_exec->globalData(), asObject(inValue)) && !asObject(inValue)->inherits(&JSArray::info));
       
   743                 if (objectStack.size() + arrayStack.size() > maximumFilterRecursion)
       
   744                     return throwError(m_exec, createStackOverflowError(m_exec));
       
   745 
       
   746                 JSObject* object = asObject(inValue);
       
   747                 objectStack.append(object);
       
   748                 indexStack.append(0);
       
   749                 propertyStack.append(PropertyNameArray(m_exec));
       
   750                 object->getOwnPropertyNames(m_exec, propertyStack.last());
       
   751                 // fallthrough
       
   752             }
       
   753             objectStartVisitMember:
       
   754             case ObjectStartVisitMember: {
       
   755                 if (!--tickCount) {
       
   756                     if (localTimeoutChecker.didTimeOut(m_exec))
       
   757                         return throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData()));
       
   758                     tickCount = localTimeoutChecker.ticksUntilNextCheck();
       
   759                 }
       
   760 
       
   761                 JSObject* object = objectStack.last();
       
   762                 uint32_t index = indexStack.last();
       
   763                 PropertyNameArray& properties = propertyStack.last();
       
   764                 if (index == properties.size()) {
       
   765                     outValue = object;
       
   766                     objectStack.removeLast();
       
   767                     indexStack.removeLast();
       
   768                     propertyStack.removeLast();
       
   769                     break;
       
   770                 }
       
   771                 PropertySlot slot;
       
   772                 if (object->getOwnPropertySlot(m_exec, properties[index], slot))
       
   773                     inValue = slot.getValue(m_exec, properties[index]);
       
   774                 else
       
   775                     inValue = jsUndefined();
       
   776 
       
   777                 // The holder may be modified by the reviver function so any lookup may throw
       
   778                 if (m_exec->hadException())
       
   779                     return jsNull();
       
   780 
       
   781                 if (inValue.isObject()) {
       
   782                     stateStack.append(ObjectEndVisitMember);
       
   783                     goto stateUnknown;
       
   784                 } else
       
   785                     outValue = inValue;
       
   786                 // fallthrough
       
   787             }
       
   788             case ObjectEndVisitMember: {
       
   789                 JSObject* object = objectStack.last();
       
   790                 Identifier prop = propertyStack.last()[indexStack.last()];
       
   791                 PutPropertySlot slot;
       
   792                 JSValue filteredValue = callReviver(object, jsString(m_exec, prop.ustring()), outValue);
       
   793                 if (filteredValue.isUndefined())
       
   794                     object->deleteProperty(m_exec, prop);
       
   795                 else
       
   796                     object->put(m_exec, prop, filteredValue, slot);
       
   797                 if (m_exec->hadException())
       
   798                     return jsNull();
       
   799                 indexStack.last()++;
       
   800                 goto objectStartVisitMember;
       
   801             }
       
   802             stateUnknown:
       
   803             case StateUnknown:
       
   804                 if (!inValue.isObject()) {
       
   805                     outValue = inValue;
       
   806                     break;
       
   807                 }
       
   808                 JSObject* object = asObject(inValue);
       
   809                 if (isJSArray(&m_exec->globalData(), object) || object->inherits(&JSArray::info))
       
   810                     goto arrayStartState;
       
   811                 goto objectStartState;
       
   812         }
       
   813         if (stateStack.isEmpty())
       
   814             break;
       
   815 
       
   816         state = stateStack.last();
       
   817         stateStack.removeLast();
       
   818 
       
   819         if (!--tickCount) {
       
   820             if (localTimeoutChecker.didTimeOut(m_exec))
       
   821                 return throwError(m_exec, createInterruptedExecutionException(&m_exec->globalData()));
       
   822             tickCount = localTimeoutChecker.ticksUntilNextCheck();
       
   823         }
       
   824     }
       
   825     JSObject* finalHolder = constructEmptyObject(m_exec);
       
   826     PutPropertySlot slot;
       
   827     finalHolder->put(m_exec, m_exec->globalData().propertyNames->emptyIdentifier, outValue, slot);
       
   828     return callReviver(finalHolder, jsEmptyString(m_exec), outValue);
       
   829 }
       
   830 
       
   831 // ECMA-262 v5 15.12.2
       
   832 EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec)
       
   833 {
       
   834     if (!exec->argumentCount())
       
   835         return throwVMError(exec, createError(exec, "JSON.parse requires at least one parameter"));
       
   836     JSValue value = exec->argument(0);
       
   837     UString source = value.toString(exec);
       
   838     if (exec->hadException())
       
   839         return JSValue::encode(jsNull());
       
   840     
       
   841     LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON);
       
   842     JSValue unfiltered = jsonParser.tryLiteralParse();
       
   843     if (!unfiltered)
       
   844         return throwVMError(exec, createSyntaxError(exec, "Unable to parse JSON string"));
       
   845     
       
   846     if (exec->argumentCount() < 2)
       
   847         return JSValue::encode(unfiltered);
       
   848     
       
   849     JSValue function = exec->argument(1);
       
   850     CallData callData;
       
   851     CallType callType = getCallData(function, callData);
       
   852     if (callType == CallTypeNone)
       
   853         return JSValue::encode(unfiltered);
       
   854     return JSValue::encode(Walker(exec, asObject(function), callType, callData).walk(unfiltered));
       
   855 }
       
   856 
       
   857 // ECMA-262 v5 15.12.3
       
   858 EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState* exec)
       
   859 {
       
   860     if (!exec->argumentCount())
       
   861         return throwVMError(exec, createError(exec, "No input to stringify"));
       
   862     JSValue value = exec->argument(0);
       
   863     JSValue replacer = exec->argument(1);
       
   864     JSValue space = exec->argument(2);
       
   865     return JSValue::encode(Stringifier(exec, replacer, space).stringify(value));
       
   866 }
       
   867 
       
   868 UString JSONStringify(ExecState* exec, JSValue value, unsigned indent)
       
   869 {
       
   870     JSValue result = Stringifier(exec, jsNull(), jsNumber(exec, indent)).stringify(value);
       
   871     if (result.isUndefinedOrNull())
       
   872         return UString();
       
   873     return result.getString(exec);
       
   874 }
       
   875 
       
   876 } // namespace JSC