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 "JSCInlines.h"
40 #include "PropertyNameArray.h"
41 #include <wtf/MathExtras.h>
42 #include <wtf/text/StringBuilder.h>
46 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(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(VM
& vm
, Structure
* structure
)
58 : JSNonFinalObject(vm
, structure
)
62 void JSONObject::finishCreation(VM
& vm
)
64 Base::finishCreation(vm
);
65 ASSERT(inherits(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::info()))
143 return jsNumber(object
->toNumber(exec
));
144 if (object
->inherits(StringObject::info()))
145 return object
->toString(exec
);
146 if (object
->inherits(BooleanObject::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::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::info()) && !asObject(name
)->inherits(StringObject::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 escapeStringToBuilder(StringBuilder
& builder
, const String
& message
)
308 if (message
.is8Bit())
309 appendStringToStringBuilder(builder
, message
.characters8(), message
.length());
311 appendStringToStringBuilder(builder
, message
.characters16(), message
.length());
314 void Stringifier::appendQuotedString(StringBuilder
& builder
, const String
& value
)
316 int length
= value
.length();
321 appendStringToStringBuilder
<LChar
>(builder
, value
.characters8(), length
);
323 appendStringToStringBuilder
<UChar
>(builder
, value
.characters16(), length
);
328 inline JSValue
Stringifier::toJSON(JSValue value
, const PropertyNameForFunctionCall
& propertyName
)
330 ASSERT(!m_exec
->hadException());
331 if (!value
.isObject() || !asObject(value
)->hasProperty(m_exec
, m_exec
->vm().propertyNames
->toJSON
))
334 JSValue toJSONFunction
= asObject(value
)->get(m_exec
, m_exec
->vm().propertyNames
->toJSON
);
335 if (m_exec
->hadException())
338 if (!toJSONFunction
.isObject())
341 JSObject
* object
= asObject(toJSONFunction
);
343 CallType callType
= object
->methodTable()->getCallData(object
, callData
);
344 if (callType
== CallTypeNone
)
347 MarkedArgumentBuffer args
;
348 args
.append(propertyName
.value(m_exec
));
349 return call(m_exec
, object
, callType
, callData
, value
, args
);
352 Stringifier::StringifyResult
Stringifier::appendStringifiedValue(StringBuilder
& builder
, JSValue value
, JSObject
* holder
, const PropertyNameForFunctionCall
& propertyName
)
354 // Call the toJSON function.
355 value
= toJSON(value
, propertyName
);
356 if (m_exec
->hadException())
357 return StringifyFailed
;
359 // Call the replacer function.
360 if (m_replacerCallType
!= CallTypeNone
) {
361 MarkedArgumentBuffer args
;
362 args
.append(propertyName
.value(m_exec
));
364 value
= call(m_exec
, m_replacer
.get(), m_replacerCallType
, m_replacerCallData
, holder
, args
);
365 if (m_exec
->hadException())
366 return StringifyFailed
;
369 if (value
.isUndefined() && !holder
->inherits(JSArray::info()))
370 return StringifyFailedDueToUndefinedValue
;
372 if (value
.isNull()) {
373 builder
.appendLiteral("null");
374 return StringifySucceeded
;
377 value
= unwrapBoxedPrimitive(m_exec
, value
);
379 if (m_exec
->hadException())
380 return StringifyFailed
;
382 if (value
.isBoolean()) {
384 builder
.appendLiteral("true");
386 builder
.appendLiteral("false");
387 return StringifySucceeded
;
391 if (value
.getString(m_exec
, stringValue
)) {
392 appendQuotedString(builder
, stringValue
);
393 return StringifySucceeded
;
396 if (value
.isNumber()) {
397 double number
= value
.asNumber();
398 if (!std::isfinite(number
))
399 builder
.appendLiteral("null");
401 builder
.append(String::numberToStringECMAScript(number
));
402 return StringifySucceeded
;
405 if (!value
.isObject())
406 return StringifyFailed
;
408 JSObject
* object
= asObject(value
);
411 if (object
->methodTable()->getCallData(object
, callData
) != CallTypeNone
) {
412 if (holder
->inherits(JSArray::info())) {
413 builder
.appendLiteral("null");
414 return StringifySucceeded
;
416 return StringifyFailedDueToUndefinedValue
;
419 // Handle cycle detection, and put the holder on the stack.
420 for (unsigned i
= 0; i
< m_holderStack
.size(); i
++) {
421 if (m_holderStack
[i
].object() == object
) {
422 m_exec
->vm().throwException(m_exec
, createTypeError(m_exec
, ASCIILiteral("JSON.stringify cannot serialize cyclic structures.")));
423 return StringifyFailed
;
426 bool holderStackWasEmpty
= m_holderStack
.isEmpty();
427 m_holderStack
.append(Holder(m_exec
->vm(), object
));
428 if (!holderStackWasEmpty
)
429 return StringifySucceeded
;
432 while (m_holderStack
.last().appendNextProperty(*this, builder
)) {
433 if (m_exec
->hadException())
434 return StringifyFailed
;
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
= makeString(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(StringBuilder
& builder
) const
466 builder
.append('\n');
467 builder
.append(m_indent
);
470 inline Stringifier::Holder::Holder(VM
& vm
, JSObject
* object
)
471 : m_object(vm
, object
)
472 , m_isArray(object
->inherits(JSArray::info()))
480 bool Stringifier::Holder::appendNextProperty(Stringifier
& stringifier
, StringBuilder
& builder
)
482 ASSERT(m_index
<= m_size
);
484 ExecState
* exec
= stringifier
.m_exec
;
486 // First time through, initialize.
489 m_isJSArray
= isJSArray(m_object
.get());
490 m_size
= m_object
->get(exec
, exec
->vm().propertyNames
->length
).toUInt32(exec
);
493 if (stringifier
.m_usingArrayReplacer
)
494 m_propertyNames
= stringifier
.m_arrayReplacerPropertyNames
.data();
496 PropertyNameArray
objectPropertyNames(exec
);
497 m_object
->methodTable()->getOwnPropertyNames(m_object
.get(), exec
, objectPropertyNames
, ExcludeDontEnumProperties
);
498 m_propertyNames
= objectPropertyNames
.releaseData();
500 m_size
= m_propertyNames
->propertyNameVector().size();
503 stringifier
.indent();
506 // Last time through, finish up and return false.
507 if (m_index
== m_size
) {
508 stringifier
.unindent();
509 if (m_size
&& builder
[builder
.length() - 1] != '{')
510 stringifier
.startNewLine(builder
);
511 builder
.append(m_isArray
? ']' : '}');
515 // Handle a single element of the array or object.
516 unsigned index
= m_index
++;
517 unsigned rollBackPoint
= 0;
518 StringifyResult stringifyResult
;
522 if (m_isJSArray
&& asArray(m_object
.get())->canGetIndexQuickly(index
))
523 value
= asArray(m_object
.get())->getIndexQuickly(index
);
525 PropertySlot
slot(m_object
.get());
526 if (m_object
->methodTable()->getOwnPropertySlotByIndex(m_object
.get(), exec
, index
, slot
)) {
527 value
= slot
.getValue(exec
, index
);
528 if (exec
->hadException())
531 value
= jsUndefined();
534 // Append the separator string.
537 stringifier
.startNewLine(builder
);
539 // Append the stringified value.
540 stringifyResult
= stringifier
.appendStringifiedValue(builder
, value
, m_object
.get(), index
);
543 PropertySlot
slot(m_object
.get());
544 Identifier
& propertyName
= m_propertyNames
->propertyNameVector()[index
];
545 if (!m_object
->methodTable()->getOwnPropertySlot(m_object
.get(), exec
, propertyName
, slot
))
547 JSValue value
= slot
.getValue(exec
, propertyName
);
548 if (exec
->hadException())
551 rollBackPoint
= builder
.length();
553 // Append the separator string.
554 if (builder
[rollBackPoint
- 1] != '{')
556 stringifier
.startNewLine(builder
);
558 // Append the property name.
559 appendQuotedString(builder
, propertyName
.string());
561 if (stringifier
.willIndent())
564 // Append the stringified value.
565 stringifyResult
= stringifier
.appendStringifiedValue(builder
, value
, m_object
.get(), propertyName
);
568 // From this point on, no access to the this pointer or to any members, because the
569 // Holder object may have moved if the call to stringify pushed a new Holder onto
572 switch (stringifyResult
) {
573 case StringifyFailed
:
574 builder
.appendLiteral("null");
576 case StringifySucceeded
:
578 case StringifyFailedDueToUndefinedValue
:
579 // This only occurs when get an undefined value for an object property.
580 // In this case we don't want the separator and property name that we
581 // already appended, so roll back.
582 builder
.resize(rollBackPoint
);
589 // ------------------------------ JSONObject --------------------------------
591 const ClassInfo
JSONObject::s_info
= { "JSON", &JSNonFinalObject::s_info
, 0, ExecState::jsonTable
, CREATE_METHOD_TABLE(JSONObject
) };
593 /* Source for JSONObject.lut.h
595 parse JSONProtoFuncParse DontEnum|Function 2
596 stringify JSONProtoFuncStringify DontEnum|Function 3
602 bool JSONObject::getOwnPropertySlot(JSObject
* object
, ExecState
* exec
, PropertyName propertyName
, PropertySlot
& slot
)
604 return getStaticFunctionSlot
<JSObject
>(exec
, ExecState::jsonTable(exec
->vm()), jsCast
<JSONObject
*>(object
), propertyName
, slot
);
609 Walker(ExecState
* exec
, Handle
<JSObject
> function
, CallType callType
, CallData callData
)
611 , m_function(exec
->vm(), function
)
612 , m_callType(callType
)
613 , m_callData(callData
)
616 JSValue
walk(JSValue unfiltered
);
618 JSValue
callReviver(JSObject
* thisObj
, JSValue property
, JSValue unfiltered
)
620 MarkedArgumentBuffer args
;
621 args
.append(property
);
622 args
.append(unfiltered
);
623 return call(m_exec
, m_function
.get(), m_callType
, m_callData
, thisObj
, args
);
629 Local
<JSObject
> m_function
;
634 // We clamp recursion well beyond anything reasonable.
635 static const unsigned maximumFilterRecursion
= 40000;
636 enum WalkerState
{ StateUnknown
, ArrayStartState
, ArrayStartVisitMember
, ArrayEndVisitMember
,
637 ObjectStartState
, ObjectStartVisitMember
, ObjectEndVisitMember
};
638 NEVER_INLINE JSValue
Walker::walk(JSValue unfiltered
)
640 Vector
<PropertyNameArray
, 16, UnsafeVectorOverflow
> propertyStack
;
641 Vector
<uint32_t, 16, UnsafeVectorOverflow
> indexStack
;
642 LocalStack
<JSObject
, 16> objectStack(m_exec
->vm());
643 LocalStack
<JSArray
, 16> arrayStack(m_exec
->vm());
645 Vector
<WalkerState
, 16, UnsafeVectorOverflow
> stateStack
;
646 WalkerState state
= StateUnknown
;
647 JSValue inValue
= unfiltered
;
648 JSValue outValue
= jsNull();
653 case ArrayStartState
: {
654 ASSERT(inValue
.isObject());
655 ASSERT(isJSArray(asObject(inValue
)) || asObject(inValue
)->inherits(JSArray::info()));
656 if (objectStack
.size() + arrayStack
.size() > maximumFilterRecursion
)
657 return throwStackOverflowError(m_exec
);
659 JSArray
* array
= asArray(inValue
);
660 arrayStack
.push(array
);
661 indexStack
.append(0);
663 arrayStartVisitMember
:
665 case ArrayStartVisitMember
: {
666 JSArray
* array
= arrayStack
.peek();
667 uint32_t index
= indexStack
.last();
668 if (index
== array
->length()) {
671 indexStack
.removeLast();
674 if (isJSArray(array
) && array
->canGetIndexQuickly(index
))
675 inValue
= array
->getIndexQuickly(index
);
677 PropertySlot
slot(array
);
678 if (array
->methodTable()->getOwnPropertySlotByIndex(array
, m_exec
, index
, slot
))
679 inValue
= slot
.getValue(m_exec
, index
);
681 inValue
= jsUndefined();
684 if (inValue
.isObject()) {
685 stateStack
.append(ArrayEndVisitMember
);
691 case ArrayEndVisitMember
: {
692 JSArray
* array
= arrayStack
.peek();
693 JSValue filteredValue
= callReviver(array
, jsString(m_exec
, String::number(indexStack
.last())), outValue
);
694 if (filteredValue
.isUndefined())
695 array
->methodTable()->deletePropertyByIndex(array
, m_exec
, indexStack
.last());
697 array
->putDirectIndex(m_exec
, indexStack
.last(), filteredValue
);
698 if (m_exec
->hadException())
701 goto arrayStartVisitMember
;
704 case ObjectStartState
: {
705 ASSERT(inValue
.isObject());
706 ASSERT(!isJSArray(asObject(inValue
)) && !asObject(inValue
)->inherits(JSArray::info()));
707 if (objectStack
.size() + arrayStack
.size() > maximumFilterRecursion
)
708 return throwStackOverflowError(m_exec
);
710 JSObject
* object
= asObject(inValue
);
711 objectStack
.push(object
);
712 indexStack
.append(0);
713 propertyStack
.append(PropertyNameArray(m_exec
));
714 object
->methodTable()->getOwnPropertyNames(object
, m_exec
, propertyStack
.last(), ExcludeDontEnumProperties
);
716 objectStartVisitMember
:
718 case ObjectStartVisitMember
: {
719 JSObject
* object
= objectStack
.peek();
720 uint32_t index
= indexStack
.last();
721 PropertyNameArray
& properties
= propertyStack
.last();
722 if (index
== properties
.size()) {
725 indexStack
.removeLast();
726 propertyStack
.removeLast();
729 PropertySlot
slot(object
);
730 if (object
->methodTable()->getOwnPropertySlot(object
, m_exec
, properties
[index
], slot
))
731 inValue
= slot
.getValue(m_exec
, properties
[index
]);
733 inValue
= jsUndefined();
735 // The holder may be modified by the reviver function so any lookup may throw
736 if (m_exec
->hadException())
739 if (inValue
.isObject()) {
740 stateStack
.append(ObjectEndVisitMember
);
746 case ObjectEndVisitMember
: {
747 JSObject
* object
= objectStack
.peek();
748 Identifier prop
= propertyStack
.last()[indexStack
.last()];
749 PutPropertySlot
slot(object
);
750 JSValue filteredValue
= callReviver(object
, jsString(m_exec
, prop
.string()), outValue
);
751 if (filteredValue
.isUndefined())
752 object
->methodTable()->deleteProperty(object
, m_exec
, prop
);
754 object
->methodTable()->put(object
, m_exec
, prop
, filteredValue
, slot
);
755 if (m_exec
->hadException())
758 goto objectStartVisitMember
;
762 if (!inValue
.isObject()) {
766 JSObject
* object
= asObject(inValue
);
767 if (isJSArray(object
) || object
->inherits(JSArray::info()))
768 goto arrayStartState
;
769 goto objectStartState
;
771 if (stateStack
.isEmpty())
774 state
= stateStack
.last();
775 stateStack
.removeLast();
777 JSObject
* finalHolder
= constructEmptyObject(m_exec
);
778 PutPropertySlot
slot(finalHolder
);
779 finalHolder
->methodTable()->put(finalHolder
, m_exec
, m_exec
->vm().propertyNames
->emptyIdentifier
, outValue
, slot
);
780 return callReviver(finalHolder
, jsEmptyString(m_exec
), outValue
);
783 // ECMA-262 v5 15.12.2
784 EncodedJSValue JSC_HOST_CALL
JSONProtoFuncParse(ExecState
* exec
)
786 if (!exec
->argumentCount())
787 return throwVMError(exec
, createError(exec
, ASCIILiteral("JSON.parse requires at least one parameter")));
788 String source
= exec
->uncheckedArgument(0).toString(exec
)->value(exec
);
789 if (exec
->hadException())
790 return JSValue::encode(jsNull());
793 LocalScope
scope(exec
->vm());
794 if (source
.is8Bit()) {
795 LiteralParser
<LChar
> jsonParser(exec
, source
.characters8(), source
.length(), StrictJSON
);
796 unfiltered
= jsonParser
.tryLiteralParse();
798 return throwVMError(exec
, createSyntaxError(exec
, jsonParser
.getErrorMessage()));
800 LiteralParser
<UChar
> jsonParser(exec
, source
.characters16(), source
.length(), StrictJSON
);
801 unfiltered
= jsonParser
.tryLiteralParse();
803 return throwVMError(exec
, createSyntaxError(exec
, jsonParser
.getErrorMessage()));
806 if (exec
->argumentCount() < 2)
807 return JSValue::encode(unfiltered
);
809 JSValue function
= exec
->uncheckedArgument(1);
811 CallType callType
= getCallData(function
, callData
);
812 if (callType
== CallTypeNone
)
813 return JSValue::encode(unfiltered
);
814 return JSValue::encode(Walker(exec
, Local
<JSObject
>(exec
->vm(), asObject(function
)), callType
, callData
).walk(unfiltered
));
817 // ECMA-262 v5 15.12.3
818 EncodedJSValue JSC_HOST_CALL
JSONProtoFuncStringify(ExecState
* exec
)
820 if (!exec
->argumentCount())
821 return throwVMError(exec
, createError(exec
, ASCIILiteral("No input to stringify")));
822 LocalScope
scope(exec
->vm());
823 Local
<Unknown
> value(exec
->vm(), exec
->uncheckedArgument(0));
824 Local
<Unknown
> replacer(exec
->vm(), exec
->argument(1));
825 Local
<Unknown
> space(exec
->vm(), exec
->argument(2));
826 JSValue result
= Stringifier(exec
, replacer
, space
).stringify(value
).get();
827 return JSValue::encode(result
);
830 JSValue
JSONParse(ExecState
* exec
, const String
& json
)
832 LocalScope
scope(exec
->vm());
835 LiteralParser
<LChar
> jsonParser(exec
, json
.characters8(), json
.length(), StrictJSON
);
836 return jsonParser
.tryLiteralParse();
839 LiteralParser
<UChar
> jsonParser(exec
, json
.characters16(), json
.length(), StrictJSON
);
840 return jsonParser
.tryLiteralParse();
843 String
JSONStringify(ExecState
* exec
, JSValue value
, unsigned indent
)
845 LocalScope
scope(exec
->vm());
846 Local
<Unknown
> result
= Stringifier(exec
, Local
<Unknown
>(exec
->vm(), jsNull()), Local
<Unknown
>(exec
->vm(), jsNumber(indent
))).stringify(Local
<Unknown
>(exec
->vm(), value
));
847 if (result
.isUndefinedOrNull())
849 return result
.getString(exec
);