[ES6] JSON.stringify should ignore object properties that have symbol values and...
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSONObject.cpp
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 "Local.h"
36 #include "LocalScope.h"
37 #include "Lookup.h"
38 #include "ObjectConstructor.h"
39 #include "JSCInlines.h"
40 #include "PropertyNameArray.h"
41 #include <wtf/MathExtras.h>
42 #include <wtf/text/StringBuilder.h>
43
44 namespace JSC {
45
46 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSONObject);
47
48 EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*);
49 EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*);
50
51 }
52
53 #include "JSONObject.lut.h"
54
55 namespace JSC {
56
57 JSONObject::JSONObject(VM& vm, Structure* structure)
58     : JSNonFinalObject(vm, structure)
59 {
60 }
61
62 void JSONObject::finishCreation(VM& vm)
63 {
64     Base::finishCreation(vm);
65     ASSERT(inherits(info()));
66 }
67
68 // PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked.
69 class PropertyNameForFunctionCall {
70 public:
71     PropertyNameForFunctionCall(const Identifier&);
72     PropertyNameForFunctionCall(unsigned);
73
74     JSValue value(ExecState*) const;
75
76 private:
77     const Identifier* m_identifier;
78     unsigned m_number;
79     mutable JSValue m_value;
80 };
81
82 class Stringifier {
83     WTF_MAKE_NONCOPYABLE(Stringifier);
84 public:
85     Stringifier(ExecState*, const Local<Unknown>& replacer, const Local<Unknown>& space);
86     Local<Unknown> stringify(Handle<Unknown>);
87
88     void visitAggregate(SlotVisitor&);
89
90 private:
91     class Holder {
92     public:
93         Holder(VM&, JSObject*);
94
95         JSObject* object() const { return m_object.get(); }
96
97         bool appendNextProperty(Stringifier&, StringBuilder&);
98
99     private:
100         Local<JSObject> m_object;
101         const bool m_isArray;
102         bool m_isJSArray;
103         unsigned m_index;
104         unsigned m_size;
105         RefPtr<PropertyNameArrayData> m_propertyNames;
106     };
107
108     friend class Holder;
109
110     JSValue toJSON(JSValue, const PropertyNameForFunctionCall&);
111     JSValue toJSONImpl(JSValue, const PropertyNameForFunctionCall&);
112
113     enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedOrSymbolValue };
114     StringifyResult appendStringifiedValue(StringBuilder&, JSValue, JSObject* holder, const PropertyNameForFunctionCall&);
115
116     bool willIndent() const;
117     void indent();
118     void unindent();
119     void startNewLine(StringBuilder&) const;
120
121     ExecState* const m_exec;
122     const Local<Unknown> m_replacer;
123     bool m_usingArrayReplacer;
124     PropertyNameArray m_arrayReplacerPropertyNames;
125     CallType m_replacerCallType;
126     CallData m_replacerCallData;
127     const String m_gap;
128
129     Vector<Holder, 16, UnsafeVectorOverflow> m_holderStack;
130     String m_repeatedGap;
131     String m_indent;
132 };
133
134 // ------------------------------ helper functions --------------------------------
135
136 static inline JSValue unwrapBoxedPrimitive(ExecState* exec, JSValue value)
137 {
138     if (!value.isObject())
139         return value;
140     JSObject* object = asObject(value);
141     if (object->inherits(NumberObject::info()))
142         return jsNumber(object->toNumber(exec));
143     if (object->inherits(StringObject::info()))
144         return object->toString(exec);
145     if (object->inherits(BooleanObject::info()))
146         return object->toPrimitive(exec);
147
148     // Do not unwrap SymbolObject to Symbol. It is not performed in the spec.
149     // http://www.ecma-international.org/ecma-262/6.0/#sec-serializejsonproperty
150     return value;
151 }
152
153 static inline String gap(ExecState* exec, JSValue space)
154 {
155     const unsigned maxGapLength = 10;
156     space = unwrapBoxedPrimitive(exec, space);
157
158     // If the space value is a number, create a gap string with that number of spaces.
159     if (space.isNumber()) {
160         double spaceCount = space.asNumber();
161         int count;
162         if (spaceCount > maxGapLength)
163             count = maxGapLength;
164         else if (!(spaceCount > 0))
165             count = 0;
166         else
167             count = static_cast<int>(spaceCount);
168         UChar spaces[maxGapLength];
169         for (int i = 0; i < count; ++i)
170             spaces[i] = ' ';
171         return String(spaces, count);
172     }
173
174     // If the space value is a string, use it as the gap string, otherwise use no gap string.
175     String spaces = space.getString(exec);
176     if (spaces.length() > maxGapLength) {
177         spaces = spaces.substringSharingImpl(0, maxGapLength);
178     }
179     return spaces;
180 }
181
182 // ------------------------------ PropertyNameForFunctionCall --------------------------------
183
184 inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(const Identifier& identifier)
185     : m_identifier(&identifier)
186 {
187 }
188
189 inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number)
190     : m_identifier(0)
191     , m_number(number)
192 {
193 }
194
195 JSValue PropertyNameForFunctionCall::value(ExecState* exec) const
196 {
197     if (!m_value) {
198         if (m_identifier)
199             m_value = jsString(exec, m_identifier->string());
200         else
201             m_value = jsNumber(m_number);
202     }
203     return m_value;
204 }
205
206 // ------------------------------ Stringifier --------------------------------
207
208 Stringifier::Stringifier(ExecState* exec, const Local<Unknown>& replacer, const Local<Unknown>& space)
209     : m_exec(exec)
210     , m_replacer(replacer)
211     , m_usingArrayReplacer(false)
212     , m_arrayReplacerPropertyNames(exec, PropertyNameMode::Strings)
213     , m_replacerCallType(CallTypeNone)
214     , m_gap(gap(exec, space.get()))
215 {
216     if (!m_replacer.isObject())
217         return;
218
219     if (m_replacer.asObject()->inherits(JSArray::info())) {
220         m_usingArrayReplacer = true;
221         Handle<JSObject> array = m_replacer.asObject();
222         unsigned length = array->get(exec, exec->vm().propertyNames->length).toUInt32(exec);
223         for (unsigned i = 0; i < length; ++i) {
224             JSValue name = array->get(exec, i);
225             if (exec->hadException())
226                 break;
227
228             if (name.isObject()) {
229                 if (!asObject(name)->inherits(NumberObject::info()) && !asObject(name)->inherits(StringObject::info()))
230                     continue;
231             } else if (!name.isNumber() && !name.isString())
232                 continue;
233
234             m_arrayReplacerPropertyNames.add(name.toString(exec)->toIdentifier(exec));
235         }
236         return;
237     }
238
239     m_replacerCallType = m_replacer.asObject()->methodTable()->getCallData(m_replacer.asObject().get(), m_replacerCallData);
240 }
241
242 Local<Unknown> Stringifier::stringify(Handle<Unknown> value)
243 {
244     JSObject* object = constructEmptyObject(m_exec);
245     if (m_exec->hadException())
246         return Local<Unknown>(m_exec->vm(), jsNull());
247
248     PropertyNameForFunctionCall emptyPropertyName(m_exec->vm().propertyNames->emptyIdentifier);
249     object->putDirect(m_exec->vm(), m_exec->vm().propertyNames->emptyIdentifier, value.get());
250
251     StringBuilder result;
252     if (appendStringifiedValue(result, value.get(), object, emptyPropertyName) != StringifySucceeded)
253         return Local<Unknown>(m_exec->vm(), jsUndefined());
254     if (m_exec->hadException())
255         return Local<Unknown>(m_exec->vm(), jsNull());
256
257     return Local<Unknown>(m_exec->vm(), jsString(m_exec, result.toString()));
258 }
259
260 ALWAYS_INLINE JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionCall& propertyName)
261 {
262     ASSERT(!m_exec->hadException());
263     if (!value.isObject() || !asObject(value)->hasProperty(m_exec, m_exec->vm().propertyNames->toJSON))
264         return value;
265     return toJSONImpl(value, propertyName);
266 }
267
268 JSValue Stringifier::toJSONImpl(JSValue value, const PropertyNameForFunctionCall& propertyName)
269 {
270     JSValue toJSONFunction = asObject(value)->get(m_exec, m_exec->vm().propertyNames->toJSON);
271     if (m_exec->hadException())
272         return jsNull();
273
274     if (!toJSONFunction.isObject())
275         return value;
276
277     JSObject* object = asObject(toJSONFunction);
278     CallData callData;
279     CallType callType = object->methodTable()->getCallData(object, callData);
280     if (callType == CallTypeNone)
281         return value;
282
283     MarkedArgumentBuffer args;
284     args.append(propertyName.value(m_exec));
285     return call(m_exec, object, callType, callData, value, args);
286 }
287
288 Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName)
289 {
290     // Call the toJSON function.
291     value = toJSON(value, propertyName);
292     if (m_exec->hadException())
293         return StringifyFailed;
294
295     // Call the replacer function.
296     if (m_replacerCallType != CallTypeNone) {
297         MarkedArgumentBuffer args;
298         args.append(propertyName.value(m_exec));
299         args.append(value);
300         value = call(m_exec, m_replacer.get(), m_replacerCallType, m_replacerCallData, holder, args);
301         if (m_exec->hadException())
302             return StringifyFailed;
303     }
304
305     if ((value.isUndefined() || value.isSymbol()) && !holder->inherits(JSArray::info()))
306         return StringifyFailedDueToUndefinedOrSymbolValue;
307
308     if (value.isNull()) {
309         builder.appendLiteral("null");
310         return StringifySucceeded;
311     }
312
313     value = unwrapBoxedPrimitive(m_exec, value);
314
315     if (m_exec->hadException())
316         return StringifyFailed;
317
318     if (value.isBoolean()) {
319         if (value.isTrue())
320             builder.appendLiteral("true");
321         else
322             builder.appendLiteral("false");
323         return StringifySucceeded;
324     }
325
326     if (value.isString()) {
327         builder.appendQuotedJSONString(asString(value)->value(m_exec));
328         return StringifySucceeded;
329     }
330
331     if (value.isNumber()) {
332         if (value.isInt32())
333             builder.appendNumber(value.asInt32());
334         else {
335             double number = value.asNumber();
336             if (!std::isfinite(number))
337                 builder.appendLiteral("null");
338             else
339                 builder.appendECMAScriptNumber(number);
340         }
341         return StringifySucceeded;
342     }
343
344     if (!value.isObject())
345         return StringifyFailed;
346
347     JSObject* object = asObject(value);
348
349     CallData callData;
350     if (object->methodTable()->getCallData(object, callData) != CallTypeNone) {
351         if (holder->inherits(JSArray::info())) {
352             builder.appendLiteral("null");
353             return StringifySucceeded;
354         }
355         return StringifyFailedDueToUndefinedOrSymbolValue;
356     }
357
358     // Handle cycle detection, and put the holder on the stack.
359     for (unsigned i = 0; i < m_holderStack.size(); i++) {
360         if (m_holderStack[i].object() == object) {
361             m_exec->vm().throwException(m_exec, createTypeError(m_exec, ASCIILiteral("JSON.stringify cannot serialize cyclic structures.")));
362             return StringifyFailed;
363         }
364     }
365     bool holderStackWasEmpty = m_holderStack.isEmpty();
366     m_holderStack.append(Holder(m_exec->vm(), object));
367     if (!holderStackWasEmpty)
368         return StringifySucceeded;
369
370     do {
371         while (m_holderStack.last().appendNextProperty(*this, builder)) {
372             if (m_exec->hadException())
373                 return StringifyFailed;
374         }
375         m_holderStack.removeLast();
376     } while (!m_holderStack.isEmpty());
377     return StringifySucceeded;
378 }
379
380 inline bool Stringifier::willIndent() const
381 {
382     return !m_gap.isEmpty();
383 }
384
385 inline void Stringifier::indent()
386 {
387     // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent.
388     unsigned newSize = m_indent.length() + m_gap.length();
389     if (newSize > m_repeatedGap.length())
390         m_repeatedGap = makeString(m_repeatedGap, m_gap);
391     ASSERT(newSize <= m_repeatedGap.length());
392     m_indent = m_repeatedGap.substringSharingImpl(0, newSize);
393 }
394
395 inline void Stringifier::unindent()
396 {
397     ASSERT(m_indent.length() >= m_gap.length());
398     m_indent = m_repeatedGap.substringSharingImpl(0, m_indent.length() - m_gap.length());
399 }
400
401 inline void Stringifier::startNewLine(StringBuilder& builder) const
402 {
403     if (m_gap.isEmpty())
404         return;
405     builder.append('\n');
406     builder.append(m_indent);
407 }
408
409 inline Stringifier::Holder::Holder(VM& vm, JSObject* object)
410     : m_object(vm, object)
411     , m_isArray(object->inherits(JSArray::info()))
412     , m_index(0)
413 #ifndef NDEBUG
414     , m_size(0)
415 #endif
416 {
417 }
418
419 bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBuilder& builder)
420 {
421     ASSERT(m_index <= m_size);
422
423     ExecState* exec = stringifier.m_exec;
424
425     // First time through, initialize.
426     if (!m_index) {
427         if (m_isArray) {
428             m_isJSArray = isJSArray(m_object.get());
429             if (m_isJSArray)
430                 m_size = asArray(m_object.get())->length();
431             else
432                 m_size = m_object->get(exec, exec->vm().propertyNames->length).toUInt32(exec);
433             builder.append('[');
434         } else {
435             if (stringifier.m_usingArrayReplacer)
436                 m_propertyNames = stringifier.m_arrayReplacerPropertyNames.data();
437             else {
438                 PropertyNameArray objectPropertyNames(exec, PropertyNameMode::Strings);
439                 m_object->methodTable()->getOwnPropertyNames(m_object.get(), exec, objectPropertyNames, EnumerationMode());
440                 m_propertyNames = objectPropertyNames.releaseData();
441             }
442             m_size = m_propertyNames->propertyNameVector().size();
443             builder.append('{');
444         }
445         stringifier.indent();
446     }
447
448     // Last time through, finish up and return false.
449     if (m_index == m_size) {
450         stringifier.unindent();
451         if (m_size && builder[builder.length() - 1] != '{')
452             stringifier.startNewLine(builder);
453         builder.append(m_isArray ? ']' : '}');
454         return false;
455     }
456
457     // Handle a single element of the array or object.
458     unsigned index = m_index++;
459     unsigned rollBackPoint = 0;
460     StringifyResult stringifyResult;
461     if (m_isArray) {
462         // Get the value.
463         JSValue value;
464         if (m_isJSArray && asArray(m_object.get())->canGetIndexQuickly(index))
465             value = asArray(m_object.get())->getIndexQuickly(index);
466         else {
467             PropertySlot slot(m_object.get());
468             if (m_object->methodTable()->getOwnPropertySlotByIndex(m_object.get(), exec, index, slot)) {
469                 value = slot.getValue(exec, index);
470                 if (exec->hadException())
471                     return false;
472             } else
473                 value = jsUndefined();
474         }
475
476         // Append the separator string.
477         if (index)
478             builder.append(',');
479         stringifier.startNewLine(builder);
480
481         // Append the stringified value.
482         stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object.get(), index);
483     } else {
484         // Get the value.
485         PropertySlot slot(m_object.get());
486         Identifier& propertyName = m_propertyNames->propertyNameVector()[index];
487         if (!m_object->methodTable()->getOwnPropertySlot(m_object.get(), exec, propertyName, slot))
488             return true;
489         JSValue value = slot.getValue(exec, propertyName);
490         if (exec->hadException())
491             return false;
492
493         rollBackPoint = builder.length();
494
495         // Append the separator string.
496         if (builder[rollBackPoint - 1] != '{')
497             builder.append(',');
498         stringifier.startNewLine(builder);
499
500         // Append the property name.
501         builder.appendQuotedJSONString(propertyName.string());
502         builder.append(':');
503         if (stringifier.willIndent())
504             builder.append(' ');
505
506         // Append the stringified value.
507         stringifyResult = stringifier.appendStringifiedValue(builder, value, m_object.get(), propertyName);
508     }
509
510     // From this point on, no access to the this pointer or to any members, because the
511     // Holder object may have moved if the call to stringify pushed a new Holder onto
512     // m_holderStack.
513
514     switch (stringifyResult) {
515         case StringifyFailed:
516             builder.appendLiteral("null");
517             break;
518         case StringifySucceeded:
519             break;
520         case StringifyFailedDueToUndefinedOrSymbolValue:
521             // This only occurs when get an undefined value or a symbol value for
522             // an object property. In this case we don't want the separator and
523             // property name that we already appended, so roll back.
524             builder.resize(rollBackPoint);
525             break;
526     }
527
528     return true;
529 }
530
531 // ------------------------------ JSONObject --------------------------------
532
533 const ClassInfo JSONObject::s_info = { "JSON", &JSNonFinalObject::s_info, &jsonTable, CREATE_METHOD_TABLE(JSONObject) };
534
535 /* Source for JSONObject.lut.h
536 @begin jsonTable
537   parse         JSONProtoFuncParse             DontEnum|Function 2
538   stringify     JSONProtoFuncStringify         DontEnum|Function 3
539 @end
540 */
541
542 // ECMA 15.8
543
544 bool JSONObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
545 {
546     return getStaticFunctionSlot<JSObject>(exec, jsonTable, jsCast<JSONObject*>(object), propertyName, slot);
547 }
548
549 class Walker {
550 public:
551     Walker(ExecState* exec, Handle<JSObject> function, CallType callType, CallData callData)
552         : m_exec(exec)
553         , m_function(exec->vm(), function)
554         , m_callType(callType)
555         , m_callData(callData)
556     {
557     }
558     JSValue walk(JSValue unfiltered);
559 private:
560     JSValue callReviver(JSObject* thisObj, JSValue property, JSValue unfiltered)
561     {
562         MarkedArgumentBuffer args;
563         args.append(property);
564         args.append(unfiltered);
565         return call(m_exec, m_function.get(), m_callType, m_callData, thisObj, args);
566     }
567
568     friend class Holder;
569
570     ExecState* m_exec;
571     Local<JSObject> m_function;
572     CallType m_callType;
573     CallData m_callData;
574 };
575
576 // We clamp recursion well beyond anything reasonable.
577 static const unsigned maximumFilterRecursion = 40000;
578 enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember, 
579                                  ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember };
580 NEVER_INLINE JSValue Walker::walk(JSValue unfiltered)
581 {
582     Vector<PropertyNameArray, 16, UnsafeVectorOverflow> propertyStack;
583     Vector<uint32_t, 16, UnsafeVectorOverflow> indexStack;
584     LocalStack<JSObject, 16> objectStack(m_exec->vm());
585     LocalStack<JSArray, 16> arrayStack(m_exec->vm());
586     
587     Vector<WalkerState, 16, UnsafeVectorOverflow> stateStack;
588     WalkerState state = StateUnknown;
589     JSValue inValue = unfiltered;
590     JSValue outValue = jsNull();
591     
592     while (1) {
593         switch (state) {
594             arrayStartState:
595             case ArrayStartState: {
596                 ASSERT(inValue.isObject());
597                 ASSERT(isJSArray(asObject(inValue)) || asObject(inValue)->inherits(JSArray::info()));
598                 if (objectStack.size() + arrayStack.size() > maximumFilterRecursion)
599                     return throwStackOverflowError(m_exec);
600
601                 JSArray* array = asArray(inValue);
602                 arrayStack.push(array);
603                 indexStack.append(0);
604             }
605             arrayStartVisitMember:
606             FALLTHROUGH;
607             case ArrayStartVisitMember: {
608                 JSArray* array = arrayStack.peek();
609                 uint32_t index = indexStack.last();
610                 if (index == array->length()) {
611                     outValue = array;
612                     arrayStack.pop();
613                     indexStack.removeLast();
614                     break;
615                 }
616                 if (isJSArray(array) && array->canGetIndexQuickly(index))
617                     inValue = array->getIndexQuickly(index);
618                 else {
619                     PropertySlot slot(array);
620                     if (array->methodTable()->getOwnPropertySlotByIndex(array, m_exec, index, slot))
621                         inValue = slot.getValue(m_exec, index);
622                     else
623                         inValue = jsUndefined();
624                 }
625                     
626                 if (inValue.isObject()) {
627                     stateStack.append(ArrayEndVisitMember);
628                     goto stateUnknown;
629                 } else
630                     outValue = inValue;
631                 FALLTHROUGH;
632             }
633             case ArrayEndVisitMember: {
634                 JSArray* array = arrayStack.peek();
635                 JSValue filteredValue = callReviver(array, jsString(m_exec, String::number(indexStack.last())), outValue);
636                 if (filteredValue.isUndefined())
637                     array->methodTable()->deletePropertyByIndex(array, m_exec, indexStack.last());
638                 else
639                     array->putDirectIndex(m_exec, indexStack.last(), filteredValue);
640                 if (m_exec->hadException())
641                     return jsNull();
642                 indexStack.last()++;
643                 goto arrayStartVisitMember;
644             }
645             objectStartState:
646             case ObjectStartState: {
647                 ASSERT(inValue.isObject());
648                 ASSERT(!isJSArray(asObject(inValue)) && !asObject(inValue)->inherits(JSArray::info()));
649                 if (objectStack.size() + arrayStack.size() > maximumFilterRecursion)
650                     return throwStackOverflowError(m_exec);
651
652                 JSObject* object = asObject(inValue);
653                 objectStack.push(object);
654                 indexStack.append(0);
655                 propertyStack.append(PropertyNameArray(m_exec, PropertyNameMode::Strings));
656                 object->methodTable()->getOwnPropertyNames(object, m_exec, propertyStack.last(), EnumerationMode());
657             }
658             objectStartVisitMember:
659             FALLTHROUGH;
660             case ObjectStartVisitMember: {
661                 JSObject* object = objectStack.peek();
662                 uint32_t index = indexStack.last();
663                 PropertyNameArray& properties = propertyStack.last();
664                 if (index == properties.size()) {
665                     outValue = object;
666                     objectStack.pop();
667                     indexStack.removeLast();
668                     propertyStack.removeLast();
669                     break;
670                 }
671                 PropertySlot slot(object);
672                 if (object->methodTable()->getOwnPropertySlot(object, m_exec, properties[index], slot))
673                     inValue = slot.getValue(m_exec, properties[index]);
674                 else
675                     inValue = jsUndefined();
676
677                 // The holder may be modified by the reviver function so any lookup may throw
678                 if (m_exec->hadException())
679                     return jsNull();
680
681                 if (inValue.isObject()) {
682                     stateStack.append(ObjectEndVisitMember);
683                     goto stateUnknown;
684                 } else
685                     outValue = inValue;
686                 FALLTHROUGH;
687             }
688             case ObjectEndVisitMember: {
689                 JSObject* object = objectStack.peek();
690                 Identifier prop = propertyStack.last()[indexStack.last()];
691                 PutPropertySlot slot(object);
692                 JSValue filteredValue = callReviver(object, jsString(m_exec, prop.string()), outValue);
693                 if (filteredValue.isUndefined())
694                     object->methodTable()->deleteProperty(object, m_exec, prop);
695                 else
696                     object->methodTable()->put(object, m_exec, prop, filteredValue, slot);
697                 if (m_exec->hadException())
698                     return jsNull();
699                 indexStack.last()++;
700                 goto objectStartVisitMember;
701             }
702             stateUnknown:
703             case StateUnknown:
704                 if (!inValue.isObject()) {
705                     outValue = inValue;
706                     break;
707                 }
708                 JSObject* object = asObject(inValue);
709                 if (isJSArray(object) || object->inherits(JSArray::info()))
710                     goto arrayStartState;
711                 goto objectStartState;
712         }
713         if (stateStack.isEmpty())
714             break;
715
716         state = stateStack.last();
717         stateStack.removeLast();
718     }
719     JSObject* finalHolder = constructEmptyObject(m_exec);
720     PutPropertySlot slot(finalHolder);
721     finalHolder->methodTable()->put(finalHolder, m_exec, m_exec->vm().propertyNames->emptyIdentifier, outValue, slot);
722     return callReviver(finalHolder, jsEmptyString(m_exec), outValue);
723 }
724
725 // ECMA-262 v5 15.12.2
726 EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec)
727 {
728     if (!exec->argumentCount())
729         return throwVMError(exec, createError(exec, ASCIILiteral("JSON.parse requires at least one parameter")));
730     StringView source = exec->uncheckedArgument(0).toString(exec)->view(exec);
731     if (exec->hadException())
732         return JSValue::encode(jsNull());
733
734     JSValue unfiltered;
735     LocalScope scope(exec->vm());
736     if (source.is8Bit()) {
737         LiteralParser<LChar> jsonParser(exec, source.characters8(), source.length(), StrictJSON);
738         unfiltered = jsonParser.tryLiteralParse();
739         if (!unfiltered)
740             return throwVMError(exec, createSyntaxError(exec, jsonParser.getErrorMessage()));
741     } else {
742         LiteralParser<UChar> jsonParser(exec, source.characters16(), source.length(), StrictJSON);
743         unfiltered = jsonParser.tryLiteralParse();
744         if (!unfiltered)
745             return throwVMError(exec, createSyntaxError(exec, jsonParser.getErrorMessage()));        
746     }
747     
748     if (exec->argumentCount() < 2)
749         return JSValue::encode(unfiltered);
750     
751     JSValue function = exec->uncheckedArgument(1);
752     CallData callData;
753     CallType callType = getCallData(function, callData);
754     if (callType == CallTypeNone)
755         return JSValue::encode(unfiltered);
756     return JSValue::encode(Walker(exec, Local<JSObject>(exec->vm(), asObject(function)), callType, callData).walk(unfiltered));
757 }
758
759 // ECMA-262 v5 15.12.3
760 EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState* exec)
761 {
762     if (!exec->argumentCount())
763         return throwVMError(exec, createError(exec, ASCIILiteral("No input to stringify")));
764     LocalScope scope(exec->vm());
765     Local<Unknown> value(exec->vm(), exec->uncheckedArgument(0));
766     Local<Unknown> replacer(exec->vm(), exec->argument(1));
767     Local<Unknown> space(exec->vm(), exec->argument(2));
768     JSValue result = Stringifier(exec, replacer, space).stringify(value).get();
769     return JSValue::encode(result);
770 }
771
772 JSValue JSONParse(ExecState* exec, const String& json)
773 {
774     LocalScope scope(exec->vm());
775
776     if (json.is8Bit()) {
777         LiteralParser<LChar> jsonParser(exec, json.characters8(), json.length(), StrictJSON);
778         return jsonParser.tryLiteralParse();
779     }
780
781     LiteralParser<UChar> jsonParser(exec, json.characters16(), json.length(), StrictJSON);
782     return jsonParser.tryLiteralParse();
783 }
784
785 String JSONStringify(ExecState* exec, JSValue value, unsigned indent)
786 {
787     LocalScope scope(exec->vm());
788     Local<Unknown> result = Stringifier(exec, Local<Unknown>(exec->vm(), jsNull()), Local<Unknown>(exec->vm(), jsNumber(indent))).stringify(Local<Unknown>(exec->vm(), value));
789     if (result.isUndefinedOrNull())
790         return String();
791     return result.getString(exec);
792 }
793
794 } // namespace JSC