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 "ObjectConstructor.h"
39 #include "Operations.h"
40 #include "PropertyNameArray.h"
41 #include <wtf/MathExtras.h>
42 #include <wtf/text/StringBuilder.h>
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
->vm(), structure
)
62 void JSONObject::finishCreation(JSGlobalObject
* globalObject
)
64 Base::finishCreation(globalObject
->vm());
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(VM
&, JSObject
*);
95 JSObject
* object() const { return m_object
.get(); }
97 bool appendNextProperty(Stringifier
&, StringBuilder
&);
100 Local
<JSObject
> m_object
;
101 const bool m_isArray
;
105 RefPtr
<PropertyNameArrayData
> m_propertyNames
;
110 static void appendQuotedString(StringBuilder
&, const String
&);
112 JSValue
toJSON(JSValue
, const PropertyNameForFunctionCall
&);
114 enum StringifyResult
{ StringifyFailed
, StringifySucceeded
, StringifyFailedDueToUndefinedValue
};
115 StringifyResult
appendStringifiedValue(StringBuilder
&, JSValue
, JSObject
* holder
, const PropertyNameForFunctionCall
&);
117 bool willIndent() const;
120 void startNewLine(StringBuilder
&) 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, UnsafeVectorOverflow
> m_holderStack
;
131 String 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 String
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 String(spaces
, count
);
172 // If the space value is a string, use it as the gap string, otherwise use no gap string.
173 String 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
->string());
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
->vm().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
->vm(), jsNull());
245 PropertyNameForFunctionCall
emptyPropertyName(m_exec
->vm().propertyNames
->emptyIdentifier
);
246 object
->putDirect(m_exec
->vm(), m_exec
->vm().propertyNames
->emptyIdentifier
, value
.get());
248 StringBuilder result
;
249 if (appendStringifiedValue(result
, value
.get(), object
, emptyPropertyName
) != StringifySucceeded
)
250 return Local
<Unknown
>(m_exec
->vm(), jsUndefined());
251 if (m_exec
->hadException())
252 return Local
<Unknown
>(m_exec
->vm(), jsNull());
254 return Local
<Unknown
>(m_exec
->vm(), jsString(m_exec
, result
.toString()));
257 template <typename CharType
>
258 static void appendStringToStringBuilder(StringBuilder
& 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', static_cast<LChar
>(hexDigits
[(ch
>> 12) & 0xF]), static_cast<LChar
>(hexDigits
[(ch
>> 8) & 0xF]), static_cast<LChar
>(hexDigits
[(ch
>> 4) & 0xF]), static_cast<LChar
>(hexDigits
[ch
& 0xF]) };
300 builder
.append(hex
, WTF_ARRAY_LENGTH(hex
));
306 void Stringifier::appendQuotedString(StringBuilder
& builder
, const String
& value
)
308 int length
= value
.length();
313 appendStringToStringBuilder
<LChar
>(builder
, value
.characters8(), length
);
315 appendStringToStringBuilder
<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
->vm().propertyNames
->toJSON
))
326 JSValue toJSONFunction
= asObject(value
)->get(m_exec
, m_exec
->vm().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(StringBuilder
& 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
.appendLiteral("null");
366 return StringifySucceeded
;
369 value
= unwrapBoxedPrimitive(m_exec
, value
);
371 if (m_exec
->hadException())
372 return StringifyFailed
;
374 if (value
.isBoolean()) {
376 builder
.appendLiteral("true");
378 builder
.appendLiteral("false");
379 return StringifySucceeded
;
383 if (value
.getString(m_exec
, stringValue
)) {
384 appendQuotedString(builder
, stringValue
);
385 return StringifySucceeded
;
388 if (value
.isNumber()) {
389 double number
= value
.asNumber();
390 if (!std::isfinite(number
))
391 builder
.appendLiteral("null");
393 builder
.append(String::numberToStringECMAScript(number
));
394 return StringifySucceeded
;
397 if (!value
.isObject())
398 return StringifyFailed
;
400 JSObject
* object
= asObject(value
);
403 if (object
->methodTable()->getCallData(object
, callData
) != CallTypeNone
) {
404 if (holder
->inherits(&JSArray::s_info
)) {
405 builder
.appendLiteral("null");
406 return StringifySucceeded
;
408 return StringifyFailedDueToUndefinedValue
;
411 // Handle cycle detection, and put the holder on the stack.
412 for (unsigned i
= 0; i
< m_holderStack
.size(); i
++) {
413 if (m_holderStack
[i
].object() == object
) {
414 throwError(m_exec
, createTypeError(m_exec
, ASCIILiteral("JSON.stringify cannot serialize cyclic structures.")));
415 return StringifyFailed
;
418 bool holderStackWasEmpty
= m_holderStack
.isEmpty();
419 m_holderStack
.append(Holder(m_exec
->vm(), object
));
420 if (!holderStackWasEmpty
)
421 return StringifySucceeded
;
424 while (m_holderStack
.last().appendNextProperty(*this, builder
)) {
425 if (m_exec
->hadException())
426 return StringifyFailed
;
428 m_holderStack
.removeLast();
429 } while (!m_holderStack
.isEmpty());
430 return StringifySucceeded
;
433 inline bool Stringifier::willIndent() const
435 return !m_gap
.isEmpty();
438 inline void Stringifier::indent()
440 // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent.
441 unsigned newSize
= m_indent
.length() + m_gap
.length();
442 if (newSize
> m_repeatedGap
.length())
443 m_repeatedGap
= makeString(m_repeatedGap
, m_gap
);
444 ASSERT(newSize
<= m_repeatedGap
.length());
445 m_indent
= m_repeatedGap
.substringSharingImpl(0, newSize
);
448 inline void Stringifier::unindent()
450 ASSERT(m_indent
.length() >= m_gap
.length());
451 m_indent
= m_repeatedGap
.substringSharingImpl(0, m_indent
.length() - m_gap
.length());
454 inline void Stringifier::startNewLine(StringBuilder
& builder
) const
458 builder
.append('\n');
459 builder
.append(m_indent
);
462 inline Stringifier::Holder::Holder(VM
& vm
, JSObject
* object
)
463 : m_object(vm
, object
)
464 , m_isArray(object
->inherits(&JSArray::s_info
))
472 bool Stringifier::Holder::appendNextProperty(Stringifier
& stringifier
, StringBuilder
& builder
)
474 ASSERT(m_index
<= m_size
);
476 ExecState
* exec
= stringifier
.m_exec
;
478 // First time through, initialize.
481 m_isJSArray
= isJSArray(m_object
.get());
482 m_size
= m_object
->get(exec
, exec
->vm().propertyNames
->length
).toUInt32(exec
);
485 if (stringifier
.m_usingArrayReplacer
)
486 m_propertyNames
= stringifier
.m_arrayReplacerPropertyNames
.data();
488 PropertyNameArray
objectPropertyNames(exec
);
489 m_object
->methodTable()->getOwnPropertyNames(m_object
.get(), exec
, objectPropertyNames
, ExcludeDontEnumProperties
);
490 m_propertyNames
= objectPropertyNames
.releaseData();
492 m_size
= m_propertyNames
->propertyNameVector().size();
495 stringifier
.indent();
498 // Last time through, finish up and return false.
499 if (m_index
== m_size
) {
500 stringifier
.unindent();
501 if (m_size
&& builder
[builder
.length() - 1] != '{')
502 stringifier
.startNewLine(builder
);
503 builder
.append(m_isArray
? ']' : '}');
507 // Handle a single element of the array or object.
508 unsigned index
= m_index
++;
509 unsigned rollBackPoint
= 0;
510 StringifyResult stringifyResult
;
514 if (m_isJSArray
&& asArray(m_object
.get())->canGetIndexQuickly(index
))
515 value
= asArray(m_object
.get())->getIndexQuickly(index
);
517 PropertySlot
slot(m_object
.get());
518 if (!m_object
->methodTable()->getOwnPropertySlotByIndex(m_object
.get(), exec
, index
, slot
))
520 if (exec
->hadException())
522 value
= slot
.getValue(exec
, index
);
525 // Append the separator string.
528 stringifier
.startNewLine(builder
);
530 // Append the stringified value.
531 stringifyResult
= stringifier
.appendStringifiedValue(builder
, value
, m_object
.get(), index
);
534 PropertySlot
slot(m_object
.get());
535 Identifier
& propertyName
= m_propertyNames
->propertyNameVector()[index
];
536 if (!m_object
->methodTable()->getOwnPropertySlot(m_object
.get(), exec
, propertyName
, slot
))
538 JSValue value
= slot
.getValue(exec
, propertyName
);
539 if (exec
->hadException())
542 rollBackPoint
= builder
.length();
544 // Append the separator string.
545 if (builder
[rollBackPoint
- 1] != '{')
547 stringifier
.startNewLine(builder
);
549 // Append the property name.
550 appendQuotedString(builder
, propertyName
.string());
552 if (stringifier
.willIndent())
555 // Append the stringified value.
556 stringifyResult
= stringifier
.appendStringifiedValue(builder
, value
, m_object
.get(), propertyName
);
559 // From this point on, no access to the this pointer or to any members, because the
560 // Holder object may have moved if the call to stringify pushed a new Holder onto
563 switch (stringifyResult
) {
564 case StringifyFailed
:
565 builder
.appendLiteral("null");
567 case StringifySucceeded
:
569 case StringifyFailedDueToUndefinedValue
:
570 // This only occurs when get an undefined value for an object property.
571 // In this case we don't want the separator and property name that we
572 // already appended, so roll back.
573 builder
.resize(rollBackPoint
);
580 // ------------------------------ JSONObject --------------------------------
582 const ClassInfo
JSONObject::s_info
= { "JSON", &JSNonFinalObject::s_info
, 0, ExecState::jsonTable
, CREATE_METHOD_TABLE(JSONObject
) };
584 /* Source for JSONObject.lut.h
586 parse JSONProtoFuncParse DontEnum|Function 2
587 stringify JSONProtoFuncStringify DontEnum|Function 3
593 bool JSONObject::getOwnPropertySlot(JSCell
* cell
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
595 return getStaticFunctionSlot
<JSObject
>(exec
, ExecState::jsonTable(exec
), jsCast
<JSONObject
*>(cell
), propertyName
, slot
);
598 bool JSONObject::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertyDescriptor
& descriptor
)
600 return getStaticFunctionDescriptor
<JSObject
>(exec
, ExecState::jsonTable(exec
), jsCast
<JSONObject
*>(object
), propertyName
, descriptor
);
605 Walker(ExecState
* exec
, Handle
<JSObject
> function
, CallType callType
, CallData callData
)
607 , m_function(exec
->vm(), function
)
608 , m_callType(callType
)
609 , m_callData(callData
)
612 JSValue
walk(JSValue unfiltered
);
614 JSValue
callReviver(JSObject
* thisObj
, JSValue property
, JSValue unfiltered
)
616 MarkedArgumentBuffer args
;
617 args
.append(property
);
618 args
.append(unfiltered
);
619 return call(m_exec
, m_function
.get(), m_callType
, m_callData
, thisObj
, args
);
625 Local
<JSObject
> m_function
;
630 // We clamp recursion well beyond anything reasonable.
631 static const unsigned maximumFilterRecursion
= 40000;
632 enum WalkerState
{ StateUnknown
, ArrayStartState
, ArrayStartVisitMember
, ArrayEndVisitMember
,
633 ObjectStartState
, ObjectStartVisitMember
, ObjectEndVisitMember
};
634 NEVER_INLINE JSValue
Walker::walk(JSValue unfiltered
)
636 Vector
<PropertyNameArray
, 16, UnsafeVectorOverflow
> propertyStack
;
637 Vector
<uint32_t, 16, UnsafeVectorOverflow
> indexStack
;
638 LocalStack
<JSObject
, 16> objectStack(m_exec
->vm());
639 LocalStack
<JSArray
, 16> arrayStack(m_exec
->vm());
641 Vector
<WalkerState
, 16, UnsafeVectorOverflow
> stateStack
;
642 WalkerState state
= StateUnknown
;
643 JSValue inValue
= unfiltered
;
644 JSValue outValue
= jsNull();
649 case ArrayStartState
: {
650 ASSERT(inValue
.isObject());
651 ASSERT(isJSArray(asObject(inValue
)) || asObject(inValue
)->inherits(&JSArray::s_info
));
652 if (objectStack
.size() + arrayStack
.size() > maximumFilterRecursion
)
653 return throwError(m_exec
, createStackOverflowError(m_exec
));
655 JSArray
* array
= asArray(inValue
);
656 arrayStack
.push(array
);
657 indexStack
.append(0);
660 arrayStartVisitMember
:
661 case ArrayStartVisitMember
: {
662 JSArray
* array
= arrayStack
.peek();
663 uint32_t index
= indexStack
.last();
664 if (index
== array
->length()) {
667 indexStack
.removeLast();
670 if (isJSArray(array
) && array
->canGetIndexQuickly(index
))
671 inValue
= array
->getIndexQuickly(index
);
674 if (array
->methodTable()->getOwnPropertySlotByIndex(array
, m_exec
, index
, slot
))
675 inValue
= slot
.getValue(m_exec
, index
);
677 inValue
= jsUndefined();
680 if (inValue
.isObject()) {
681 stateStack
.append(ArrayEndVisitMember
);
687 case ArrayEndVisitMember
: {
688 JSArray
* array
= arrayStack
.peek();
689 JSValue filteredValue
= callReviver(array
, jsString(m_exec
, String::number(indexStack
.last())), outValue
);
690 if (filteredValue
.isUndefined())
691 array
->methodTable()->deletePropertyByIndex(array
, m_exec
, indexStack
.last());
693 array
->putDirectIndex(m_exec
, indexStack
.last(), filteredValue
);
694 if (m_exec
->hadException())
697 goto arrayStartVisitMember
;
700 case ObjectStartState
: {
701 ASSERT(inValue
.isObject());
702 ASSERT(!isJSArray(asObject(inValue
)) && !asObject(inValue
)->inherits(&JSArray::s_info
));
703 if (objectStack
.size() + arrayStack
.size() > maximumFilterRecursion
)
704 return throwError(m_exec
, createStackOverflowError(m_exec
));
706 JSObject
* object
= asObject(inValue
);
707 objectStack
.push(object
);
708 indexStack
.append(0);
709 propertyStack
.append(PropertyNameArray(m_exec
));
710 object
->methodTable()->getOwnPropertyNames(object
, m_exec
, propertyStack
.last(), ExcludeDontEnumProperties
);
713 objectStartVisitMember
:
714 case ObjectStartVisitMember
: {
715 JSObject
* object
= objectStack
.peek();
716 uint32_t index
= indexStack
.last();
717 PropertyNameArray
& properties
= propertyStack
.last();
718 if (index
== properties
.size()) {
721 indexStack
.removeLast();
722 propertyStack
.removeLast();
726 if (object
->methodTable()->getOwnPropertySlot(object
, m_exec
, properties
[index
], slot
))
727 inValue
= slot
.getValue(m_exec
, properties
[index
]);
729 inValue
= jsUndefined();
731 // The holder may be modified by the reviver function so any lookup may throw
732 if (m_exec
->hadException())
735 if (inValue
.isObject()) {
736 stateStack
.append(ObjectEndVisitMember
);
742 case ObjectEndVisitMember
: {
743 JSObject
* object
= objectStack
.peek();
744 Identifier prop
= propertyStack
.last()[indexStack
.last()];
745 PutPropertySlot slot
;
746 JSValue filteredValue
= callReviver(object
, jsString(m_exec
, prop
.string()), outValue
);
747 if (filteredValue
.isUndefined())
748 object
->methodTable()->deleteProperty(object
, m_exec
, prop
);
750 object
->methodTable()->put(object
, m_exec
, prop
, filteredValue
, slot
);
751 if (m_exec
->hadException())
754 goto objectStartVisitMember
;
758 if (!inValue
.isObject()) {
762 JSObject
* object
= asObject(inValue
);
763 if (isJSArray(object
) || object
->inherits(&JSArray::s_info
))
764 goto arrayStartState
;
765 goto objectStartState
;
767 if (stateStack
.isEmpty())
770 state
= stateStack
.last();
771 stateStack
.removeLast();
773 JSObject
* finalHolder
= constructEmptyObject(m_exec
);
774 PutPropertySlot slot
;
775 finalHolder
->methodTable()->put(finalHolder
, m_exec
, m_exec
->vm().propertyNames
->emptyIdentifier
, outValue
, slot
);
776 return callReviver(finalHolder
, jsEmptyString(m_exec
), outValue
);
779 // ECMA-262 v5 15.12.2
780 EncodedJSValue JSC_HOST_CALL
JSONProtoFuncParse(ExecState
* exec
)
782 if (!exec
->argumentCount())
783 return throwVMError(exec
, createError(exec
, ASCIILiteral("JSON.parse requires at least one parameter")));
784 String source
= exec
->argument(0).toString(exec
)->value(exec
);
785 if (exec
->hadException())
786 return JSValue::encode(jsNull());
789 LocalScope
scope(exec
->vm());
790 if (source
.is8Bit()) {
791 LiteralParser
<LChar
> jsonParser(exec
, source
.characters8(), source
.length(), StrictJSON
);
792 unfiltered
= jsonParser
.tryLiteralParse();
794 return throwVMError(exec
, createSyntaxError(exec
, jsonParser
.getErrorMessage()));
796 LiteralParser
<UChar
> jsonParser(exec
, source
.characters16(), source
.length(), StrictJSON
);
797 unfiltered
= jsonParser
.tryLiteralParse();
799 return throwVMError(exec
, createSyntaxError(exec
, jsonParser
.getErrorMessage()));
802 if (exec
->argumentCount() < 2)
803 return JSValue::encode(unfiltered
);
805 JSValue function
= exec
->argument(1);
807 CallType callType
= getCallData(function
, callData
);
808 if (callType
== CallTypeNone
)
809 return JSValue::encode(unfiltered
);
810 return JSValue::encode(Walker(exec
, Local
<JSObject
>(exec
->vm(), asObject(function
)), callType
, callData
).walk(unfiltered
));
813 // ECMA-262 v5 15.12.3
814 EncodedJSValue JSC_HOST_CALL
JSONProtoFuncStringify(ExecState
* exec
)
816 if (!exec
->argumentCount())
817 return throwVMError(exec
, createError(exec
, ASCIILiteral("No input to stringify")));
818 LocalScope
scope(exec
->vm());
819 Local
<Unknown
> value(exec
->vm(), exec
->argument(0));
820 Local
<Unknown
> replacer(exec
->vm(), exec
->argument(1));
821 Local
<Unknown
> space(exec
->vm(), exec
->argument(2));
822 return JSValue::encode(Stringifier(exec
, replacer
, space
).stringify(value
).get());
825 String
JSONStringify(ExecState
* exec
, JSValue value
, unsigned indent
)
827 LocalScope
scope(exec
->vm());
828 Local
<Unknown
> result
= Stringifier(exec
, Local
<Unknown
>(exec
->vm(), jsNull()), Local
<Unknown
>(exec
->vm(), jsNumber(indent
))).stringify(Local
<Unknown
>(exec
->vm(), value
));
829 if (result
.isUndefinedOrNull())
831 return result
.getString(exec
);