2 * Copyright (C) 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "JSONObject.h"
29 #include "BooleanObject.h"
31 #include "ExceptionHelpers.h"
33 #include "JSGlobalObject.h"
34 #include "LiteralParser.h"
36 #include "LocalScope.h"
38 #include "PropertyNameArray.h"
39 #include "UStringBuilder.h"
40 #include "UStringConcatenate.h"
41 #include <wtf/MathExtras.h>
45 ASSERT_CLASS_FITS_IN_CELL(JSONObject
);
46 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSONObject
);
48 static EncodedJSValue JSC_HOST_CALL
JSONProtoFuncParse(ExecState
*);
49 static EncodedJSValue JSC_HOST_CALL
JSONProtoFuncStringify(ExecState
*);
53 #include "JSONObject.lut.h"
57 JSONObject::JSONObject(JSGlobalObject
* globalObject
, Structure
* structure
)
58 : JSNonFinalObject(globalObject
->globalData(), structure
)
62 void JSONObject::finishCreation(JSGlobalObject
* globalObject
)
64 Base::finishCreation(globalObject
->globalData());
65 ASSERT(inherits(&s_info
));
68 // PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked.
69 class PropertyNameForFunctionCall
{
71 PropertyNameForFunctionCall(const Identifier
&);
72 PropertyNameForFunctionCall(unsigned);
74 JSValue
value(ExecState
*) const;
77 const Identifier
* m_identifier
;
79 mutable JSValue m_value
;
83 WTF_MAKE_NONCOPYABLE(Stringifier
);
85 Stringifier(ExecState
*, const Local
<Unknown
>& replacer
, const Local
<Unknown
>& space
);
86 Local
<Unknown
> stringify(Handle
<Unknown
>);
88 void visitAggregate(SlotVisitor
&);
93 Holder(JSGlobalData
&, JSObject
*);
95 JSObject
* object() const { return m_object
.get(); }
97 bool appendNextProperty(Stringifier
&, UStringBuilder
&);
100 Local
<JSObject
> m_object
;
101 const bool m_isArray
;
105 RefPtr
<PropertyNameArrayData
> m_propertyNames
;
110 static void appendQuotedString(UStringBuilder
&, const UString
&);
112 JSValue
toJSON(JSValue
, const PropertyNameForFunctionCall
&);
114 enum StringifyResult
{ StringifyFailed
, StringifySucceeded
, StringifyFailedDueToUndefinedValue
};
115 StringifyResult
appendStringifiedValue(UStringBuilder
&, JSValue
, JSObject
* holder
, const PropertyNameForFunctionCall
&);
117 bool willIndent() const;
120 void startNewLine(UStringBuilder
&) const;
122 ExecState
* const m_exec
;
123 const Local
<Unknown
> m_replacer
;
124 bool m_usingArrayReplacer
;
125 PropertyNameArray m_arrayReplacerPropertyNames
;
126 CallType m_replacerCallType
;
127 CallData m_replacerCallData
;
130 Vector
<Holder
, 16> m_holderStack
;
131 UString m_repeatedGap
;
135 // ------------------------------ helper functions --------------------------------
137 static inline JSValue
unwrapBoxedPrimitive(ExecState
* exec
, JSValue value
)
139 if (!value
.isObject())
141 JSObject
* object
= asObject(value
);
142 if (object
->inherits(&NumberObject::s_info
))
143 return jsNumber(object
->toNumber(exec
));
144 if (object
->inherits(&StringObject::s_info
))
145 return object
->toString(exec
);
146 if (object
->inherits(&BooleanObject::s_info
))
147 return object
->toPrimitive(exec
);
151 static inline UString
gap(ExecState
* exec
, JSValue space
)
153 const unsigned maxGapLength
= 10;
154 space
= unwrapBoxedPrimitive(exec
, space
);
156 // If the space value is a number, create a gap string with that number of spaces.
157 if (space
.isNumber()) {
158 double spaceCount
= space
.asNumber();
160 if (spaceCount
> maxGapLength
)
161 count
= maxGapLength
;
162 else if (!(spaceCount
> 0))
165 count
= static_cast<int>(spaceCount
);
166 UChar spaces
[maxGapLength
];
167 for (int i
= 0; i
< count
; ++i
)
169 return UString(spaces
, count
);
172 // If the space value is a string, use it as the gap string, otherwise use no gap string.
173 UString spaces
= space
.getString(exec
);
174 if (spaces
.length() > maxGapLength
) {
175 spaces
= spaces
.substringSharingImpl(0, maxGapLength
);
180 // ------------------------------ PropertyNameForFunctionCall --------------------------------
182 inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(const Identifier
& identifier
)
183 : m_identifier(&identifier
)
187 inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number
)
193 JSValue
PropertyNameForFunctionCall::value(ExecState
* exec
) const
197 m_value
= jsString(exec
, m_identifier
->ustring());
199 m_value
= jsNumber(m_number
);
204 // ------------------------------ Stringifier --------------------------------
206 Stringifier::Stringifier(ExecState
* exec
, const Local
<Unknown
>& replacer
, const Local
<Unknown
>& space
)
208 , m_replacer(replacer
)
209 , m_usingArrayReplacer(false)
210 , m_arrayReplacerPropertyNames(exec
)
211 , m_replacerCallType(CallTypeNone
)
212 , m_gap(gap(exec
, space
.get()))
214 if (!m_replacer
.isObject())
217 if (m_replacer
.asObject()->inherits(&JSArray::s_info
)) {
218 m_usingArrayReplacer
= true;
219 Handle
<JSObject
> array
= m_replacer
.asObject();
220 unsigned length
= array
->get(exec
, exec
->globalData().propertyNames
->length
).toUInt32(exec
);
221 for (unsigned i
= 0; i
< length
; ++i
) {
222 JSValue name
= array
->get(exec
, i
);
223 if (exec
->hadException())
226 if (name
.isObject()) {
227 if (!asObject(name
)->inherits(&NumberObject::s_info
) && !asObject(name
)->inherits(&StringObject::s_info
))
231 m_arrayReplacerPropertyNames
.add(Identifier(exec
, name
.toString(exec
)->value(exec
)));
236 m_replacerCallType
= m_replacer
.asObject()->methodTable()->getCallData(m_replacer
.asObject().get(), m_replacerCallData
);
239 Local
<Unknown
> Stringifier::stringify(Handle
<Unknown
> value
)
241 JSObject
* object
= constructEmptyObject(m_exec
);
242 if (m_exec
->hadException())
243 return Local
<Unknown
>(m_exec
->globalData(), jsNull());
245 PropertyNameForFunctionCall
emptyPropertyName(m_exec
->globalData().propertyNames
->emptyIdentifier
);
246 object
->putDirect(m_exec
->globalData(), m_exec
->globalData().propertyNames
->emptyIdentifier
, value
.get());
248 UStringBuilder result
;
249 if (appendStringifiedValue(result
, value
.get(), object
, emptyPropertyName
) != StringifySucceeded
)
250 return Local
<Unknown
>(m_exec
->globalData(), jsUndefined());
251 if (m_exec
->hadException())
252 return Local
<Unknown
>(m_exec
->globalData(), jsNull());
254 return Local
<Unknown
>(m_exec
->globalData(), jsString(m_exec
, result
.toUString()));
257 template <typename CharType
>
258 static void appendStringToUStringBuilder(UStringBuilder
& builder
, const CharType
* data
, int length
)
260 for (int i
= 0; i
< length
; ++i
) {
262 while (i
< length
&& (data
[i
] > 0x1F && data
[i
] != '"' && data
[i
] != '\\'))
264 builder
.append(data
+ start
, i
- start
);
269 builder
.append('\\');
273 builder
.append('\\');
277 builder
.append('\\');
281 builder
.append('\\');
285 builder
.append('\\');
289 builder
.append('\\');
293 builder
.append('\\');
294 builder
.append('\\');
297 static const char hexDigits
[] = "0123456789abcdef";
299 LChar hex
[] = { '\\', 'u', hexDigits
[(ch
>> 12) & 0xF], hexDigits
[(ch
>> 8) & 0xF], hexDigits
[(ch
>> 4) & 0xF], hexDigits
[ch
& 0xF] };
300 builder
.append(hex
, WTF_ARRAY_LENGTH(hex
));
306 void Stringifier::appendQuotedString(UStringBuilder
& builder
, const UString
& value
)
308 int length
= value
.length();
313 appendStringToUStringBuilder
<LChar
>(builder
, value
.characters8(), length
);
315 appendStringToUStringBuilder
<UChar
>(builder
, value
.characters16(), length
);
320 inline JSValue
Stringifier::toJSON(JSValue value
, const PropertyNameForFunctionCall
& propertyName
)
322 ASSERT(!m_exec
->hadException());
323 if (!value
.isObject() || !asObject(value
)->hasProperty(m_exec
, m_exec
->globalData().propertyNames
->toJSON
))
326 JSValue toJSONFunction
= asObject(value
)->get(m_exec
, m_exec
->globalData().propertyNames
->toJSON
);
327 if (m_exec
->hadException())
330 if (!toJSONFunction
.isObject())
333 JSObject
* object
= asObject(toJSONFunction
);
335 CallType callType
= object
->methodTable()->getCallData(object
, callData
);
336 if (callType
== CallTypeNone
)
339 MarkedArgumentBuffer args
;
340 args
.append(propertyName
.value(m_exec
));
341 return call(m_exec
, object
, callType
, callData
, value
, args
);
344 Stringifier::StringifyResult
Stringifier::appendStringifiedValue(UStringBuilder
& builder
, JSValue value
, JSObject
* holder
, const PropertyNameForFunctionCall
& propertyName
)
346 // Call the toJSON function.
347 value
= toJSON(value
, propertyName
);
348 if (m_exec
->hadException())
349 return StringifyFailed
;
351 // Call the replacer function.
352 if (m_replacerCallType
!= CallTypeNone
) {
353 MarkedArgumentBuffer args
;
354 args
.append(propertyName
.value(m_exec
));
356 value
= call(m_exec
, m_replacer
.get(), m_replacerCallType
, m_replacerCallData
, holder
, args
);
357 if (m_exec
->hadException())
358 return StringifyFailed
;
361 if (value
.isUndefined() && !holder
->inherits(&JSArray::s_info
))
362 return StringifyFailedDueToUndefinedValue
;
364 if (value
.isNull()) {
365 builder
.append("null");
366 return StringifySucceeded
;
369 value
= unwrapBoxedPrimitive(m_exec
, value
);
371 if (m_exec
->hadException())
372 return StringifyFailed
;
374 if (value
.isBoolean()) {
375 builder
.append(value
.isTrue() ? "true" : "false");
376 return StringifySucceeded
;
380 if (value
.getString(m_exec
, stringValue
)) {
381 appendQuotedString(builder
, stringValue
);
382 return StringifySucceeded
;
385 if (value
.isNumber()) {
386 double number
= value
.asNumber();
387 if (!isfinite(number
))
388 builder
.append("null");
390 builder
.append(UString::number(number
));
391 return StringifySucceeded
;
394 if (!value
.isObject())
395 return StringifyFailed
;
397 JSObject
* object
= asObject(value
);
400 if (object
->methodTable()->getCallData(object
, callData
) != CallTypeNone
) {
401 if (holder
->inherits(&JSArray::s_info
)) {
402 builder
.append("null");
403 return StringifySucceeded
;
405 return StringifyFailedDueToUndefinedValue
;
408 // Handle cycle detection, and put the holder on the stack.
409 for (unsigned i
= 0; i
< m_holderStack
.size(); i
++) {
410 if (m_holderStack
[i
].object() == object
) {
411 throwError(m_exec
, createTypeError(m_exec
, "JSON.stringify cannot serialize cyclic structures."));
412 return StringifyFailed
;
415 bool holderStackWasEmpty
= m_holderStack
.isEmpty();
416 m_holderStack
.append(Holder(m_exec
->globalData(), object
));
417 if (!holderStackWasEmpty
)
418 return StringifySucceeded
;
420 // If this is the outermost call, then loop to handle everything on the holder stack.
421 TimeoutChecker
localTimeoutChecker(m_exec
->globalData().timeoutChecker
);
422 localTimeoutChecker
.reset();
423 unsigned tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
425 while (m_holderStack
.last().appendNextProperty(*this, builder
)) {
426 if (m_exec
->hadException())
427 return StringifyFailed
;
429 if (localTimeoutChecker
.didTimeOut(m_exec
)) {
430 throwError(m_exec
, createInterruptedExecutionException(&m_exec
->globalData()));
431 return StringifyFailed
;
433 tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
436 m_holderStack
.removeLast();
437 } while (!m_holderStack
.isEmpty());
438 return StringifySucceeded
;
441 inline bool Stringifier::willIndent() const
443 return !m_gap
.isEmpty();
446 inline void Stringifier::indent()
448 // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent.
449 unsigned newSize
= m_indent
.length() + m_gap
.length();
450 if (newSize
> m_repeatedGap
.length())
451 m_repeatedGap
= makeUString(m_repeatedGap
, m_gap
);
452 ASSERT(newSize
<= m_repeatedGap
.length());
453 m_indent
= m_repeatedGap
.substringSharingImpl(0, newSize
);
456 inline void Stringifier::unindent()
458 ASSERT(m_indent
.length() >= m_gap
.length());
459 m_indent
= m_repeatedGap
.substringSharingImpl(0, m_indent
.length() - m_gap
.length());
462 inline void Stringifier::startNewLine(UStringBuilder
& builder
) const
466 builder
.append('\n');
467 builder
.append(m_indent
);
470 inline Stringifier::Holder::Holder(JSGlobalData
& globalData
, JSObject
* object
)
471 : m_object(globalData
, object
)
472 , m_isArray(object
->inherits(&JSArray::s_info
))
477 bool Stringifier::Holder::appendNextProperty(Stringifier
& stringifier
, UStringBuilder
& builder
)
479 ASSERT(m_index
<= m_size
);
481 ExecState
* exec
= stringifier
.m_exec
;
483 // First time through, initialize.
486 m_isJSArray
= isJSArray(m_object
.get());
487 m_size
= m_object
->get(exec
, exec
->globalData().propertyNames
->length
).toUInt32(exec
);
490 if (stringifier
.m_usingArrayReplacer
)
491 m_propertyNames
= stringifier
.m_arrayReplacerPropertyNames
.data();
493 PropertyNameArray
objectPropertyNames(exec
);
494 m_object
->methodTable()->getOwnPropertyNames(m_object
.get(), exec
, objectPropertyNames
, ExcludeDontEnumProperties
);
495 m_propertyNames
= objectPropertyNames
.releaseData();
497 m_size
= m_propertyNames
->propertyNameVector().size();
500 stringifier
.indent();
503 // Last time through, finish up and return false.
504 if (m_index
== m_size
) {
505 stringifier
.unindent();
506 if (m_size
&& builder
[builder
.length() - 1] != '{')
507 stringifier
.startNewLine(builder
);
508 builder
.append(m_isArray
? ']' : '}');
512 // Handle a single element of the array or object.
513 unsigned index
= m_index
++;
514 unsigned rollBackPoint
= 0;
515 StringifyResult stringifyResult
;
519 if (m_isJSArray
&& asArray(m_object
.get())->canGetIndex(index
))
520 value
= asArray(m_object
.get())->getIndex(index
);
522 PropertySlot
slot(m_object
.get());
523 if (!m_object
->methodTable()->getOwnPropertySlotByIndex(m_object
.get(), exec
, index
, slot
))
525 if (exec
->hadException())
527 value
= slot
.getValue(exec
, index
);
530 // Append the separator string.
533 stringifier
.startNewLine(builder
);
535 // Append the stringified value.
536 stringifyResult
= stringifier
.appendStringifiedValue(builder
, value
, m_object
.get(), index
);
539 PropertySlot
slot(m_object
.get());
540 Identifier
& propertyName
= m_propertyNames
->propertyNameVector()[index
];
541 if (!m_object
->methodTable()->getOwnPropertySlot(m_object
.get(), exec
, propertyName
, slot
))
543 JSValue value
= slot
.getValue(exec
, propertyName
);
544 if (exec
->hadException())
547 rollBackPoint
= builder
.length();
549 // Append the separator string.
550 if (builder
[rollBackPoint
- 1] != '{')
552 stringifier
.startNewLine(builder
);
554 // Append the property name.
555 appendQuotedString(builder
, propertyName
.ustring());
557 if (stringifier
.willIndent())
560 // Append the stringified value.
561 stringifyResult
= stringifier
.appendStringifiedValue(builder
, value
, m_object
.get(), propertyName
);
564 // From this point on, no access to the this pointer or to any members, because the
565 // Holder object may have moved if the call to stringify pushed a new Holder onto
568 switch (stringifyResult
) {
569 case StringifyFailed
:
570 builder
.append("null");
572 case StringifySucceeded
:
574 case StringifyFailedDueToUndefinedValue
:
575 // This only occurs when get an undefined value for an object property.
576 // In this case we don't want the separator and property name that we
577 // already appended, so roll back.
578 builder
.resize(rollBackPoint
);
585 // ------------------------------ JSONObject --------------------------------
587 const ClassInfo
JSONObject::s_info
= { "JSON", &JSNonFinalObject::s_info
, 0, ExecState::jsonTable
, CREATE_METHOD_TABLE(JSONObject
) };
589 /* Source for JSONObject.lut.h
591 parse JSONProtoFuncParse DontEnum|Function 2
592 stringify JSONProtoFuncStringify DontEnum|Function 3
598 bool JSONObject::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
600 return getStaticFunctionSlot
<JSObject
>(exec
, ExecState::jsonTable(exec
), jsCast
<JSONObject
*>(cell
), propertyName
, slot
);
603 bool JSONObject::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
605 return getStaticFunctionDescriptor
<JSObject
>(exec
, ExecState::jsonTable(exec
), jsCast
<JSONObject
*>(object
), propertyName
, descriptor
);
610 Walker(ExecState
* exec
, Handle
<JSObject
> function
, CallType callType
, CallData callData
)
612 , m_function(exec
->globalData(), function
)
613 , m_callType(callType
)
614 , m_callData(callData
)
617 JSValue
walk(JSValue unfiltered
);
619 JSValue
callReviver(JSObject
* thisObj
, JSValue property
, JSValue unfiltered
)
621 MarkedArgumentBuffer args
;
622 args
.append(property
);
623 args
.append(unfiltered
);
624 return call(m_exec
, m_function
.get(), m_callType
, m_callData
, thisObj
, args
);
630 Local
<JSObject
> m_function
;
635 // We clamp recursion well beyond anything reasonable, but we also have a timeout check
636 // to guard against "infinite" execution by inserting arbitrarily large objects.
637 static const unsigned maximumFilterRecursion
= 40000;
638 enum WalkerState
{ StateUnknown
, ArrayStartState
, ArrayStartVisitMember
, ArrayEndVisitMember
,
639 ObjectStartState
, ObjectStartVisitMember
, ObjectEndVisitMember
};
640 NEVER_INLINE JSValue
Walker::walk(JSValue unfiltered
)
642 Vector
<PropertyNameArray
, 16> propertyStack
;
643 Vector
<uint32_t, 16> indexStack
;
644 LocalStack
<JSObject
, 16> objectStack(m_exec
->globalData());
645 LocalStack
<JSArray
, 16> arrayStack(m_exec
->globalData());
647 Vector
<WalkerState
, 16> stateStack
;
648 WalkerState state
= StateUnknown
;
649 JSValue inValue
= unfiltered
;
650 JSValue outValue
= jsNull();
652 TimeoutChecker
localTimeoutChecker(m_exec
->globalData().timeoutChecker
);
653 localTimeoutChecker
.reset();
654 unsigned tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
658 case ArrayStartState
: {
659 ASSERT(inValue
.isObject());
660 ASSERT(isJSArray(asObject(inValue
)) || asObject(inValue
)->inherits(&JSArray::s_info
));
661 if (objectStack
.size() + arrayStack
.size() > maximumFilterRecursion
)
662 return throwError(m_exec
, createStackOverflowError(m_exec
));
664 JSArray
* array
= asArray(inValue
);
665 arrayStack
.push(array
);
666 indexStack
.append(0);
669 arrayStartVisitMember
:
670 case ArrayStartVisitMember
: {
672 if (localTimeoutChecker
.didTimeOut(m_exec
))
673 return throwError(m_exec
, createInterruptedExecutionException(&m_exec
->globalData()));
674 tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
677 JSArray
* array
= arrayStack
.peek();
678 uint32_t index
= indexStack
.last();
679 if (index
== array
->length()) {
682 indexStack
.removeLast();
685 if (isJSArray(array
) && array
->canGetIndex(index
))
686 inValue
= array
->getIndex(index
);
689 if (array
->methodTable()->getOwnPropertySlotByIndex(array
, m_exec
, index
, slot
))
690 inValue
= slot
.getValue(m_exec
, index
);
692 inValue
= jsUndefined();
695 if (inValue
.isObject()) {
696 stateStack
.append(ArrayEndVisitMember
);
702 case ArrayEndVisitMember
: {
703 JSArray
* array
= arrayStack
.peek();
704 JSValue filteredValue
= callReviver(array
, jsString(m_exec
, UString::number(indexStack
.last())), outValue
);
705 if (filteredValue
.isUndefined())
706 array
->methodTable()->deletePropertyByIndex(array
, m_exec
, indexStack
.last());
708 array
->putDirectIndex(m_exec
, indexStack
.last(), filteredValue
, false);
709 if (m_exec
->hadException())
712 goto arrayStartVisitMember
;
715 case ObjectStartState
: {
716 ASSERT(inValue
.isObject());
717 ASSERT(!isJSArray(asObject(inValue
)) && !asObject(inValue
)->inherits(&JSArray::s_info
));
718 if (objectStack
.size() + arrayStack
.size() > maximumFilterRecursion
)
719 return throwError(m_exec
, createStackOverflowError(m_exec
));
721 JSObject
* object
= asObject(inValue
);
722 objectStack
.push(object
);
723 indexStack
.append(0);
724 propertyStack
.append(PropertyNameArray(m_exec
));
725 object
->methodTable()->getOwnPropertyNames(object
, m_exec
, propertyStack
.last(), ExcludeDontEnumProperties
);
728 objectStartVisitMember
:
729 case ObjectStartVisitMember
: {
731 if (localTimeoutChecker
.didTimeOut(m_exec
))
732 return throwError(m_exec
, createInterruptedExecutionException(&m_exec
->globalData()));
733 tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
736 JSObject
* object
= objectStack
.peek();
737 uint32_t index
= indexStack
.last();
738 PropertyNameArray
& properties
= propertyStack
.last();
739 if (index
== properties
.size()) {
742 indexStack
.removeLast();
743 propertyStack
.removeLast();
747 if (object
->methodTable()->getOwnPropertySlot(object
, m_exec
, properties
[index
], slot
))
748 inValue
= slot
.getValue(m_exec
, properties
[index
]);
750 inValue
= jsUndefined();
752 // The holder may be modified by the reviver function so any lookup may throw
753 if (m_exec
->hadException())
756 if (inValue
.isObject()) {
757 stateStack
.append(ObjectEndVisitMember
);
763 case ObjectEndVisitMember
: {
764 JSObject
* object
= objectStack
.peek();
765 Identifier prop
= propertyStack
.last()[indexStack
.last()];
766 PutPropertySlot slot
;
767 JSValue filteredValue
= callReviver(object
, jsString(m_exec
, prop
.ustring()), outValue
);
768 if (filteredValue
.isUndefined())
769 object
->methodTable()->deleteProperty(object
, m_exec
, prop
);
771 object
->methodTable()->put(object
, m_exec
, prop
, filteredValue
, slot
);
772 if (m_exec
->hadException())
775 goto objectStartVisitMember
;
779 if (!inValue
.isObject()) {
783 JSObject
* object
= asObject(inValue
);
784 if (isJSArray(object
) || object
->inherits(&JSArray::s_info
))
785 goto arrayStartState
;
786 goto objectStartState
;
788 if (stateStack
.isEmpty())
791 state
= stateStack
.last();
792 stateStack
.removeLast();
795 if (localTimeoutChecker
.didTimeOut(m_exec
))
796 return throwError(m_exec
, createInterruptedExecutionException(&m_exec
->globalData()));
797 tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
800 JSObject
* finalHolder
= constructEmptyObject(m_exec
);
801 PutPropertySlot slot
;
802 finalHolder
->methodTable()->put(finalHolder
, m_exec
, m_exec
->globalData().propertyNames
->emptyIdentifier
, outValue
, slot
);
803 return callReviver(finalHolder
, jsEmptyString(m_exec
), outValue
);
806 // ECMA-262 v5 15.12.2
807 EncodedJSValue JSC_HOST_CALL
JSONProtoFuncParse(ExecState
* exec
)
809 if (!exec
->argumentCount())
810 return throwVMError(exec
, createError(exec
, "JSON.parse requires at least one parameter"));
811 UString source
= exec
->argument(0).toString(exec
)->value(exec
);
812 if (exec
->hadException())
813 return JSValue::encode(jsNull());
816 LocalScope
scope(exec
->globalData());
817 if (source
.is8Bit()) {
818 LiteralParser
<LChar
> jsonParser(exec
, source
.characters8(), source
.length(), StrictJSON
);
819 unfiltered
= jsonParser
.tryLiteralParse();
821 return throwVMError(exec
, createSyntaxError(exec
, jsonParser
.getErrorMessage()));
823 LiteralParser
<UChar
> jsonParser(exec
, source
.characters16(), source
.length(), StrictJSON
);
824 unfiltered
= jsonParser
.tryLiteralParse();
826 return throwVMError(exec
, createSyntaxError(exec
, jsonParser
.getErrorMessage()));
829 if (exec
->argumentCount() < 2)
830 return JSValue::encode(unfiltered
);
832 JSValue function
= exec
->argument(1);
834 CallType callType
= getCallData(function
, callData
);
835 if (callType
== CallTypeNone
)
836 return JSValue::encode(unfiltered
);
837 return JSValue::encode(Walker(exec
, Local
<JSObject
>(exec
->globalData(), asObject(function
)), callType
, callData
).walk(unfiltered
));
840 // ECMA-262 v5 15.12.3
841 EncodedJSValue JSC_HOST_CALL
JSONProtoFuncStringify(ExecState
* exec
)
843 if (!exec
->argumentCount())
844 return throwVMError(exec
, createError(exec
, "No input to stringify"));
845 LocalScope
scope(exec
->globalData());
846 Local
<Unknown
> value(exec
->globalData(), exec
->argument(0));
847 Local
<Unknown
> replacer(exec
->globalData(), exec
->argument(1));
848 Local
<Unknown
> space(exec
->globalData(), exec
->argument(2));
849 return JSValue::encode(Stringifier(exec
, replacer
, space
).stringify(value
).get());
852 UString
JSONStringify(ExecState
* exec
, JSValue value
, unsigned indent
)
854 LocalScope
scope(exec
->globalData());
855 Local
<Unknown
> result
= Stringifier(exec
, Local
<Unknown
>(exec
->globalData(), jsNull()), Local
<Unknown
>(exec
->globalData(), jsNumber(indent
))).stringify(Local
<Unknown
>(exec
->globalData(), value
));
856 if (result
.isUndefinedOrNull())
858 return result
.getString(exec
);