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 "LiteralParser.h"
34 #include "PropertyNameArray.h"
35 #include "StringBuilder.h"
36 #include <wtf/MathExtras.h>
40 ASSERT_CLASS_FITS_IN_CELL(JSONObject
);
42 static JSValue JSC_HOST_CALL
JSONProtoFuncParse(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
43 static JSValue JSC_HOST_CALL
JSONProtoFuncStringify(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
47 #include "JSONObject.lut.h"
51 // PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked.
52 class PropertyNameForFunctionCall
{
54 PropertyNameForFunctionCall(const Identifier
&);
55 PropertyNameForFunctionCall(unsigned);
57 JSValue
value(ExecState
*) const;
60 const Identifier
* m_identifier
;
62 mutable JSValue m_value
;
65 class Stringifier
: public Noncopyable
{
67 Stringifier(ExecState
*, JSValue replacer
, JSValue space
);
69 JSValue
stringify(JSValue
);
71 void markAggregate(MarkStack
&);
78 JSObject
* object() const { return m_object
; }
80 bool appendNextProperty(Stringifier
&, StringBuilder
&);
83 JSObject
* const m_object
;
88 RefPtr
<PropertyNameArrayData
> m_propertyNames
;
93 static void appendQuotedString(StringBuilder
&, const UString
&);
95 JSValue
toJSON(JSValue
, const PropertyNameForFunctionCall
&);
97 enum StringifyResult
{ StringifyFailed
, StringifySucceeded
, StringifyFailedDueToUndefinedValue
};
98 StringifyResult
appendStringifiedValue(StringBuilder
&, JSValue
, JSObject
* holder
, const PropertyNameForFunctionCall
&);
100 bool willIndent() const;
103 void startNewLine(StringBuilder
&) const;
105 Stringifier
* const m_nextStringifierToMark
;
106 ExecState
* const m_exec
;
107 const JSValue m_replacer
;
108 bool m_usingArrayReplacer
;
109 PropertyNameArray m_arrayReplacerPropertyNames
;
110 CallType m_replacerCallType
;
111 CallData m_replacerCallData
;
114 HashSet
<JSObject
*> m_holderCycleDetector
;
115 Vector
<Holder
, 16> m_holderStack
;
116 UString m_repeatedGap
;
120 // ------------------------------ helper functions --------------------------------
122 static inline JSValue
unwrapBoxedPrimitive(ExecState
* exec
, JSValue value
)
124 if (!value
.isObject())
126 JSObject
* object
= asObject(value
);
127 if (object
->inherits(&NumberObject::info
))
128 return jsNumber(exec
, object
->toNumber(exec
));
129 if (object
->inherits(&StringObject::info
))
130 return jsString(exec
, object
->toString(exec
));
131 if (object
->inherits(&BooleanObject::info
))
132 return object
->toPrimitive(exec
);
136 static inline UString
gap(ExecState
* exec
, JSValue space
)
138 const int maxGapLength
= 10;
139 space
= unwrapBoxedPrimitive(exec
, space
);
141 // If the space value is a number, create a gap string with that number of spaces.
143 if (space
.getNumber(spaceCount
)) {
145 if (spaceCount
> maxGapLength
)
146 count
= maxGapLength
;
147 else if (!(spaceCount
> 0))
150 count
= static_cast<int>(spaceCount
);
151 UChar spaces
[maxGapLength
];
152 for (int i
= 0; i
< count
; ++i
)
154 return UString(spaces
, count
);
157 // If the space value is a string, use it as the gap string, otherwise use no gap string.
158 UString spaces
= space
.getString(exec
);
159 if (spaces
.size() > maxGapLength
) {
160 spaces
= spaces
.substr(0, maxGapLength
);
165 // ------------------------------ PropertyNameForFunctionCall --------------------------------
167 inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(const Identifier
& identifier
)
168 : m_identifier(&identifier
)
172 inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number
)
178 JSValue
PropertyNameForFunctionCall::value(ExecState
* exec
) const
182 m_value
= jsString(exec
, m_identifier
->ustring());
184 m_value
= jsNumber(exec
, m_number
);
189 // ------------------------------ Stringifier --------------------------------
191 Stringifier::Stringifier(ExecState
* exec
, JSValue replacer
, JSValue space
)
192 : m_nextStringifierToMark(exec
->globalData().firstStringifierToMark
)
194 , m_replacer(replacer
)
195 , m_usingArrayReplacer(false)
196 , m_arrayReplacerPropertyNames(exec
)
197 , m_replacerCallType(CallTypeNone
)
198 , m_gap(gap(exec
, space
))
200 exec
->globalData().firstStringifierToMark
= this;
202 if (!m_replacer
.isObject())
205 if (asObject(m_replacer
)->inherits(&JSArray::info
)) {
206 m_usingArrayReplacer
= true;
207 JSObject
* array
= asObject(m_replacer
);
208 unsigned length
= array
->get(exec
, exec
->globalData().propertyNames
->length
).toUInt32(exec
);
209 for (unsigned i
= 0; i
< length
; ++i
) {
210 JSValue name
= array
->get(exec
, i
);
211 if (exec
->hadException())
214 UString propertyName
;
215 if (name
.getString(exec
, propertyName
)) {
216 m_arrayReplacerPropertyNames
.add(Identifier(exec
, propertyName
));
221 if (name
.getNumber(value
)) {
222 m_arrayReplacerPropertyNames
.add(Identifier::from(exec
, value
));
226 if (name
.isObject()) {
227 if (!asObject(name
)->inherits(&NumberObject::info
) && !asObject(name
)->inherits(&StringObject::info
))
229 propertyName
= name
.toString(exec
);
230 if (exec
->hadException())
232 m_arrayReplacerPropertyNames
.add(Identifier(exec
, propertyName
));
238 m_replacerCallType
= asObject(m_replacer
)->getCallData(m_replacerCallData
);
241 Stringifier::~Stringifier()
243 ASSERT(m_exec
->globalData().firstStringifierToMark
== this);
244 m_exec
->globalData().firstStringifierToMark
= m_nextStringifierToMark
;
247 void Stringifier::markAggregate(MarkStack
& markStack
)
249 for (Stringifier
* stringifier
= this; stringifier
; stringifier
= stringifier
->m_nextStringifierToMark
) {
250 size_t size
= m_holderStack
.size();
251 for (size_t i
= 0; i
< size
; ++i
)
252 markStack
.append(m_holderStack
[i
].object());
256 JSValue
Stringifier::stringify(JSValue value
)
258 JSObject
* object
= constructEmptyObject(m_exec
);
259 if (m_exec
->hadException())
262 PropertyNameForFunctionCall
emptyPropertyName(m_exec
->globalData().propertyNames
->emptyIdentifier
);
263 object
->putDirect(m_exec
->globalData().propertyNames
->emptyIdentifier
, value
);
265 StringBuilder result
;
266 if (appendStringifiedValue(result
, value
, object
, emptyPropertyName
) != StringifySucceeded
)
267 return jsUndefined();
268 if (m_exec
->hadException())
271 return jsString(m_exec
, result
.release());
274 void Stringifier::appendQuotedString(StringBuilder
& builder
, const UString
& value
)
276 int length
= value
.size();
278 // String length plus 2 for quote marks plus 8 so we can accomodate a few escaped characters.
279 builder
.reserveCapacity(builder
.size() + length
+ 2 + 8);
283 const UChar
* data
= value
.data();
284 for (int i
= 0; i
< length
; ++i
) {
286 while (i
< length
&& (data
[i
] > 0x1F && data
[i
] != '"' && data
[i
] != '\\'))
288 builder
.append(data
+ start
, i
- start
);
293 builder
.append('\\');
297 builder
.append('\\');
301 builder
.append('\\');
305 builder
.append('\\');
309 builder
.append('\\');
313 builder
.append('\\');
317 builder
.append('\\');
318 builder
.append('\\');
321 static const char hexDigits
[] = "0123456789abcdef";
323 UChar hex
[] = { '\\', 'u', hexDigits
[(ch
>> 12) & 0xF], hexDigits
[(ch
>> 8) & 0xF], hexDigits
[(ch
>> 4) & 0xF], hexDigits
[ch
& 0xF] };
324 builder
.append(hex
, sizeof(hex
) / sizeof(UChar
));
332 inline JSValue
Stringifier::toJSON(JSValue value
, const PropertyNameForFunctionCall
& propertyName
)
334 ASSERT(!m_exec
->hadException());
335 if (!value
.isObject() || !asObject(value
)->hasProperty(m_exec
, m_exec
->globalData().propertyNames
->toJSON
))
338 JSValue toJSONFunction
= asObject(value
)->get(m_exec
, m_exec
->globalData().propertyNames
->toJSON
);
339 if (m_exec
->hadException())
342 if (!toJSONFunction
.isObject())
345 JSObject
* object
= asObject(toJSONFunction
);
347 CallType callType
= object
->getCallData(callData
);
348 if (callType
== CallTypeNone
)
351 JSValue list
[] = { propertyName
.value(m_exec
) };
352 ArgList
args(list
, sizeof(list
) / sizeof(JSValue
));
353 return call(m_exec
, object
, callType
, callData
, value
, args
);
356 Stringifier::StringifyResult
Stringifier::appendStringifiedValue(StringBuilder
& builder
, JSValue value
, JSObject
* holder
, const PropertyNameForFunctionCall
& propertyName
)
358 // Call the toJSON function.
359 value
= toJSON(value
, propertyName
);
360 if (m_exec
->hadException())
361 return StringifyFailed
;
363 // Call the replacer function.
364 if (m_replacerCallType
!= CallTypeNone
) {
365 JSValue list
[] = { propertyName
.value(m_exec
), value
};
366 ArgList
args(list
, sizeof(list
) / sizeof(JSValue
));
367 value
= call(m_exec
, m_replacer
, m_replacerCallType
, m_replacerCallData
, holder
, args
);
368 if (m_exec
->hadException())
369 return StringifyFailed
;
372 if (value
.isUndefined() && !holder
->inherits(&JSArray::info
))
373 return StringifyFailedDueToUndefinedValue
;
375 if (value
.isNull()) {
376 builder
.append("null");
377 return StringifySucceeded
;
380 value
= unwrapBoxedPrimitive(m_exec
, value
);
382 if (m_exec
->hadException())
383 return StringifyFailed
;
385 if (value
.isBoolean()) {
386 builder
.append(value
.getBoolean() ? "true" : "false");
387 return StringifySucceeded
;
391 if (value
.getString(m_exec
, stringValue
)) {
392 appendQuotedString(builder
, stringValue
);
393 return StringifySucceeded
;
397 if (value
.getNumber(numericValue
)) {
398 if (!isfinite(numericValue
))
399 builder
.append("null");
401 builder
.append(UString::from(numericValue
));
402 return StringifySucceeded
;
405 if (!value
.isObject())
406 return StringifyFailed
;
408 JSObject
* object
= asObject(value
);
411 if (object
->getCallData(callData
) != CallTypeNone
) {
412 if (holder
->inherits(&JSArray::info
)) {
413 builder
.append("null");
414 return StringifySucceeded
;
416 return StringifyFailedDueToUndefinedValue
;
419 // Handle cycle detection, and put the holder on the stack.
420 if (!m_holderCycleDetector
.add(object
).second
) {
421 throwError(m_exec
, TypeError
, "JSON.stringify cannot serialize cyclic structures.");
422 return StringifyFailed
;
424 bool holderStackWasEmpty
= m_holderStack
.isEmpty();
425 m_holderStack
.append(object
);
426 if (!holderStackWasEmpty
)
427 return StringifySucceeded
;
429 // If this is the outermost call, then loop to handle everything on the holder stack.
430 TimeoutChecker
localTimeoutChecker(m_exec
->globalData().timeoutChecker
);
431 localTimeoutChecker
.reset();
432 unsigned tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
434 while (m_holderStack
.last().appendNextProperty(*this, builder
)) {
435 if (m_exec
->hadException())
436 return StringifyFailed
;
438 if (localTimeoutChecker
.didTimeOut(m_exec
)) {
439 m_exec
->setException(createInterruptedExecutionException(&m_exec
->globalData()));
440 return StringifyFailed
;
442 tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
445 m_holderCycleDetector
.remove(m_holderStack
.last().object());
446 m_holderStack
.removeLast();
447 } while (!m_holderStack
.isEmpty());
448 return StringifySucceeded
;
451 inline bool Stringifier::willIndent() const
453 return !m_gap
.isEmpty();
456 inline void Stringifier::indent()
458 // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent.
459 int newSize
= m_indent
.size() + m_gap
.size();
460 if (newSize
> m_repeatedGap
.size())
461 m_repeatedGap
= makeString(m_repeatedGap
, m_gap
);
462 ASSERT(newSize
<= m_repeatedGap
.size());
463 m_indent
= m_repeatedGap
.substr(0, newSize
);
466 inline void Stringifier::unindent()
468 ASSERT(m_indent
.size() >= m_gap
.size());
469 m_indent
= m_repeatedGap
.substr(0, m_indent
.size() - m_gap
.size());
472 inline void Stringifier::startNewLine(StringBuilder
& builder
) const
476 builder
.append('\n');
477 builder
.append(m_indent
);
480 inline Stringifier::Holder::Holder(JSObject
* object
)
482 , m_isArray(object
->inherits(&JSArray::info
))
487 bool Stringifier::Holder::appendNextProperty(Stringifier
& stringifier
, StringBuilder
& builder
)
489 ASSERT(m_index
<= m_size
);
491 ExecState
* exec
= stringifier
.m_exec
;
493 // First time through, initialize.
496 m_isJSArray
= isJSArray(&exec
->globalData(), m_object
);
497 m_size
= m_object
->get(exec
, exec
->globalData().propertyNames
->length
).toUInt32(exec
);
500 if (stringifier
.m_usingArrayReplacer
)
501 m_propertyNames
= stringifier
.m_arrayReplacerPropertyNames
.data();
503 PropertyNameArray
objectPropertyNames(exec
);
504 m_object
->getOwnPropertyNames(exec
, objectPropertyNames
);
505 m_propertyNames
= objectPropertyNames
.releaseData();
507 m_size
= m_propertyNames
->propertyNameVector().size();
510 stringifier
.indent();
513 // Last time through, finish up and return false.
514 if (m_index
== m_size
) {
515 stringifier
.unindent();
516 if (m_size
&& builder
[builder
.size() - 1] != '{')
517 stringifier
.startNewLine(builder
);
518 builder
.append(m_isArray
? ']' : '}');
522 // Handle a single element of the array or object.
523 unsigned index
= m_index
++;
524 unsigned rollBackPoint
= 0;
525 StringifyResult stringifyResult
;
529 if (m_isJSArray
&& asArray(m_object
)->canGetIndex(index
))
530 value
= asArray(m_object
)->getIndex(index
);
532 PropertySlot
slot(m_object
);
533 if (!m_object
->getOwnPropertySlot(exec
, index
, slot
))
535 if (exec
->hadException())
537 value
= slot
.getValue(exec
, index
);
540 // Append the separator string.
543 stringifier
.startNewLine(builder
);
545 // Append the stringified value.
546 stringifyResult
= stringifier
.appendStringifiedValue(builder
, value
, m_object
, index
);
549 PropertySlot
slot(m_object
);
550 Identifier
& propertyName
= m_propertyNames
->propertyNameVector()[index
];
551 if (!m_object
->getOwnPropertySlot(exec
, propertyName
, slot
))
553 JSValue value
= slot
.getValue(exec
, propertyName
);
554 if (exec
->hadException())
557 rollBackPoint
= builder
.size();
559 // Append the separator string.
560 if (builder
[rollBackPoint
- 1] != '{')
562 stringifier
.startNewLine(builder
);
564 // Append the property name.
565 appendQuotedString(builder
, propertyName
.ustring());
567 if (stringifier
.willIndent())
570 // Append the stringified value.
571 stringifyResult
= stringifier
.appendStringifiedValue(builder
, value
, m_object
, propertyName
);
574 // From this point on, no access to the this pointer or to any members, because the
575 // Holder object may have moved if the call to stringify pushed a new Holder onto
578 switch (stringifyResult
) {
579 case StringifyFailed
:
580 builder
.append("null");
582 case StringifySucceeded
:
584 case StringifyFailedDueToUndefinedValue
:
585 // This only occurs when get an undefined value for an object property.
586 // In this case we don't want the separator and property name that we
587 // already appended, so roll back.
588 builder
.resize(rollBackPoint
);
595 // ------------------------------ JSONObject --------------------------------
597 const ClassInfo
JSONObject::info
= { "JSON", 0, 0, ExecState::jsonTable
};
599 /* Source for JSONObject.lut.h
601 parse JSONProtoFuncParse DontEnum|Function 1
602 stringify JSONProtoFuncStringify DontEnum|Function 1
608 bool JSONObject::getOwnPropertySlot(ExecState
* exec
, const Identifier
& propertyName
, PropertySlot
& slot
)
610 return getStaticFunctionSlot
<JSObject
>(exec
, ExecState::jsonTable(exec
), this, propertyName
, slot
);
613 bool JSONObject::getOwnPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
615 return getStaticFunctionDescriptor
<JSObject
>(exec
, ExecState::jsonTable(exec
), this, propertyName
, descriptor
);
618 void JSONObject::markStringifiers(MarkStack
& markStack
, Stringifier
* stringifier
)
620 stringifier
->markAggregate(markStack
);
625 Walker(ExecState
* exec
, JSObject
* function
, CallType callType
, CallData callData
)
627 , m_function(function
)
628 , m_callType(callType
)
629 , m_callData(callData
)
632 JSValue
walk(JSValue unfiltered
);
634 JSValue
callReviver(JSObject
* thisObj
, JSValue property
, JSValue unfiltered
)
636 JSValue args
[] = { property
, unfiltered
};
637 ArgList
argList(args
, 2);
638 return call(m_exec
, m_function
, m_callType
, m_callData
, thisObj
, argList
);
644 JSObject
* m_function
;
649 // We clamp recursion well beyond anything reasonable, but we also have a timeout check
650 // to guard against "infinite" execution by inserting arbitrarily large objects.
651 static const unsigned maximumFilterRecursion
= 40000;
652 enum WalkerState
{ StateUnknown
, ArrayStartState
, ArrayStartVisitMember
, ArrayEndVisitMember
,
653 ObjectStartState
, ObjectStartVisitMember
, ObjectEndVisitMember
};
654 NEVER_INLINE JSValue
Walker::walk(JSValue unfiltered
)
656 Vector
<PropertyNameArray
, 16> propertyStack
;
657 Vector
<uint32_t, 16> indexStack
;
658 Vector
<JSObject
*, 16> objectStack
;
659 Vector
<JSArray
*, 16> arrayStack
;
661 Vector
<WalkerState
, 16> stateStack
;
662 WalkerState state
= StateUnknown
;
663 JSValue inValue
= unfiltered
;
664 JSValue outValue
= jsNull();
666 TimeoutChecker
localTimeoutChecker(m_exec
->globalData().timeoutChecker
);
667 localTimeoutChecker
.reset();
668 unsigned tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
672 case ArrayStartState
: {
673 ASSERT(inValue
.isObject());
674 ASSERT(isJSArray(&m_exec
->globalData(), asObject(inValue
)) || asObject(inValue
)->inherits(&JSArray::info
));
675 if (objectStack
.size() + arrayStack
.size() > maximumFilterRecursion
) {
676 m_exec
->setException(createStackOverflowError(m_exec
));
677 return jsUndefined();
680 JSArray
* array
= asArray(inValue
);
681 arrayStack
.append(array
);
682 indexStack
.append(0);
685 arrayStartVisitMember
:
686 case ArrayStartVisitMember
: {
688 if (localTimeoutChecker
.didTimeOut(m_exec
)) {
689 m_exec
->setException(createInterruptedExecutionException(&m_exec
->globalData()));
690 return jsUndefined();
692 tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
695 JSArray
* array
= arrayStack
.last();
696 uint32_t index
= indexStack
.last();
697 if (index
== array
->length()) {
699 arrayStack
.removeLast();
700 indexStack
.removeLast();
703 if (isJSArray(&m_exec
->globalData(), array
) && array
->canGetIndex(index
))
704 inValue
= array
->getIndex(index
);
707 if (array
->getOwnPropertySlot(m_exec
, index
, slot
))
708 inValue
= slot
.getValue(m_exec
, index
);
710 inValue
= jsUndefined();
713 if (inValue
.isObject()) {
714 stateStack
.append(ArrayEndVisitMember
);
720 case ArrayEndVisitMember
: {
721 JSArray
* array
= arrayStack
.last();
722 JSValue filteredValue
= callReviver(array
, jsString(m_exec
, UString::from(indexStack
.last())), outValue
);
723 if (filteredValue
.isUndefined())
724 array
->deleteProperty(m_exec
, indexStack
.last());
726 if (isJSArray(&m_exec
->globalData(), array
) && array
->canSetIndex(indexStack
.last()))
727 array
->setIndex(indexStack
.last(), filteredValue
);
729 array
->put(m_exec
, indexStack
.last(), filteredValue
);
731 if (m_exec
->hadException())
734 goto arrayStartVisitMember
;
737 case ObjectStartState
: {
738 ASSERT(inValue
.isObject());
739 ASSERT(!isJSArray(&m_exec
->globalData(), asObject(inValue
)) && !asObject(inValue
)->inherits(&JSArray::info
));
740 if (objectStack
.size() + arrayStack
.size() > maximumFilterRecursion
) {
741 m_exec
->setException(createStackOverflowError(m_exec
));
742 return jsUndefined();
745 JSObject
* object
= asObject(inValue
);
746 objectStack
.append(object
);
747 indexStack
.append(0);
748 propertyStack
.append(PropertyNameArray(m_exec
));
749 object
->getOwnPropertyNames(m_exec
, propertyStack
.last());
752 objectStartVisitMember
:
753 case ObjectStartVisitMember
: {
755 if (localTimeoutChecker
.didTimeOut(m_exec
)) {
756 m_exec
->setException(createInterruptedExecutionException(&m_exec
->globalData()));
757 return jsUndefined();
759 tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
762 JSObject
* object
= objectStack
.last();
763 uint32_t index
= indexStack
.last();
764 PropertyNameArray
& properties
= propertyStack
.last();
765 if (index
== properties
.size()) {
767 objectStack
.removeLast();
768 indexStack
.removeLast();
769 propertyStack
.removeLast();
773 if (object
->getOwnPropertySlot(m_exec
, properties
[index
], slot
))
774 inValue
= slot
.getValue(m_exec
, properties
[index
]);
776 inValue
= jsUndefined();
778 // The holder may be modified by the reviver function so any lookup may throw
779 if (m_exec
->hadException())
782 if (inValue
.isObject()) {
783 stateStack
.append(ObjectEndVisitMember
);
789 case ObjectEndVisitMember
: {
790 JSObject
* object
= objectStack
.last();
791 Identifier prop
= propertyStack
.last()[indexStack
.last()];
792 PutPropertySlot slot
;
793 JSValue filteredValue
= callReviver(object
, jsString(m_exec
, prop
.ustring()), outValue
);
794 if (filteredValue
.isUndefined())
795 object
->deleteProperty(m_exec
, prop
);
797 object
->put(m_exec
, prop
, filteredValue
, slot
);
798 if (m_exec
->hadException())
801 goto objectStartVisitMember
;
805 if (!inValue
.isObject()) {
809 JSObject
* object
= asObject(inValue
);
810 if (isJSArray(&m_exec
->globalData(), object
) || object
->inherits(&JSArray::info
))
811 goto arrayStartState
;
812 goto objectStartState
;
814 if (stateStack
.isEmpty())
817 state
= stateStack
.last();
818 stateStack
.removeLast();
821 if (localTimeoutChecker
.didTimeOut(m_exec
)) {
822 m_exec
->setException(createInterruptedExecutionException(&m_exec
->globalData()));
823 return jsUndefined();
825 tickCount
= localTimeoutChecker
.ticksUntilNextCheck();
828 JSObject
* finalHolder
= constructEmptyObject(m_exec
);
829 PutPropertySlot slot
;
830 finalHolder
->put(m_exec
, m_exec
->globalData().propertyNames
->emptyIdentifier
, outValue
, slot
);
831 return callReviver(finalHolder
, jsEmptyString(m_exec
), outValue
);
834 // ECMA-262 v5 15.12.2
835 JSValue JSC_HOST_CALL
JSONProtoFuncParse(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
838 return throwError(exec
, GeneralError
, "JSON.parse requires at least one parameter");
839 JSValue value
= args
.at(0);
840 UString source
= value
.toString(exec
);
841 if (exec
->hadException())
844 LiteralParser
jsonParser(exec
, source
, LiteralParser::StrictJSON
);
845 JSValue unfiltered
= jsonParser
.tryLiteralParse();
847 return throwError(exec
, SyntaxError
, "Unable to parse JSON string");
852 JSValue function
= args
.at(1);
854 CallType callType
= function
.getCallData(callData
);
855 if (callType
== CallTypeNone
)
857 return Walker(exec
, asObject(function
), callType
, callData
).walk(unfiltered
);
860 // ECMA-262 v5 15.12.3
861 JSValue JSC_HOST_CALL
JSONProtoFuncStringify(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
864 return throwError(exec
, GeneralError
, "No input to stringify");
865 JSValue value
= args
.at(0);
866 JSValue replacer
= args
.at(1);
867 JSValue space
= args
.at(2);
868 return Stringifier(exec
, replacer
, space
).stringify(value
);