1/*
2 * Copyright (C) 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "JSONObject.h"
28
29#include "BooleanObject.h"
30#include "Error.h"
31#include "ExceptionHelpers.h"
32#include "JSArray.h"
33#include "LiteralParser.h"
34#include "PropertyNameArray.h"
35#include "StringBuilder.h"
36#include <wtf/MathExtras.h>
37
38namespace JSC {
39
40ASSERT_CLASS_FITS_IN_CELL(JSONObject);
41
42static JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState*, JSObject*, JSValue, const ArgList&);
43static JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState*, JSObject*, JSValue, const ArgList&);
44
45}
46
47#include "JSONObject.lut.h"
48
49namespace JSC {
50
51// PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked.
52class PropertyNameForFunctionCall {
53public:
54 PropertyNameForFunctionCall(const Identifier&);
55 PropertyNameForFunctionCall(unsigned);
56
57 JSValue value(ExecState*) const;
58
59private:
60 const Identifier* m_identifier;
61 unsigned m_number;
62 mutable JSValue m_value;
63};
64
65class Stringifier : public Noncopyable {
66public:
67 Stringifier(ExecState*, JSValue replacer, JSValue space);
68 ~Stringifier();
69 JSValue stringify(JSValue);
70
71 void markAggregate(MarkStack&);
72
73private:
74 class Holder {
75 public:
76 Holder(JSObject*);
77
78 JSObject* object() const { return m_object; }
79
80 bool appendNextProperty(Stringifier&, StringBuilder&);
81
82 private:
83 JSObject* const m_object;
84 const bool m_isArray;
85 bool m_isJSArray;
86 unsigned m_index;
87 unsigned m_size;
88 RefPtr<PropertyNameArrayData> m_propertyNames;
89 };
90
91 friend class Holder;
92
93 static void appendQuotedString(StringBuilder&, const UString&);
94
95 JSValue toJSON(JSValue, const PropertyNameForFunctionCall&);
96
97 enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedValue };
98 StringifyResult appendStringifiedValue(StringBuilder&, JSValue, JSObject* holder, const PropertyNameForFunctionCall&);
99
100 bool willIndent() const;
101 void indent();
102 void unindent();
103 void startNewLine(StringBuilder&) const;
104
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;
112 const UString m_gap;
113
114 HashSet<JSObject*> m_holderCycleDetector;
115 Vector<Holder, 16> m_holderStack;
116 UString m_repeatedGap;
117 UString m_indent;
118};
119
120// ------------------------------ helper functions --------------------------------
121
122static inline JSValue unwrapBoxedPrimitive(ExecState* exec, JSValue value)
123{
124 if (!value.isObject())
125 return value;
126 JSObject* object = asObject(value);
127 if (object->inherits(info: &NumberObject::info))
128 return jsNumber(exec, d: object->toNumber(exec));
129 if (object->inherits(info: &StringObject::info))
130 return jsString(exec, s: object->toString(exec));
131 if (object->inherits(info: &BooleanObject::info))
132 return object->toPrimitive(exec);
133 return value;
134}
135
136static inline UString gap(ExecState* exec, JSValue space)
137{
138 const int maxGapLength = 10;
139 space = unwrapBoxedPrimitive(exec, value: space);
140
141 // If the space value is a number, create a gap string with that number of spaces.
142 double spaceCount;
143 if (space.getNumber(result&: spaceCount)) {
144 int count;
145 if (spaceCount > maxGapLength)
146 count = maxGapLength;
147 else if (!(spaceCount > 0))
148 count = 0;
149 else
150 count = static_cast<int>(spaceCount);
151 UChar spaces[maxGapLength];
152 for (int i = 0; i < count; ++i)
153 spaces[i] = ' ';
154 return UString(spaces, count);
155 }
156
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(pos: 0, len: maxGapLength);
161 }
162 return spaces;
163}
164
165// ------------------------------ PropertyNameForFunctionCall --------------------------------
166
167inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(const Identifier& identifier)
168 : m_identifier(&identifier)
169{
170}
171
172inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number)
173 : m_identifier(0)
174 , m_number(number)
175{
176}
177
178JSValue PropertyNameForFunctionCall::value(ExecState* exec) const
179{
180 if (!m_value) {
181 if (m_identifier)
182 m_value = jsString(exec, s: m_identifier->ustring());
183 else
184 m_value = jsNumber(exec, i: m_number);
185 }
186 return m_value;
187}
188
189// ------------------------------ Stringifier --------------------------------
190
191Stringifier::Stringifier(ExecState* exec, JSValue replacer, JSValue space)
192 : m_nextStringifierToMark(exec->globalData().firstStringifierToMark)
193 , m_exec(exec)
194 , m_replacer(replacer)
195 , m_usingArrayReplacer(false)
196 , m_arrayReplacerPropertyNames(exec)
197 , m_replacerCallType(CallTypeNone)
198 , m_gap(gap(exec, space))
199{
200 exec->globalData().firstStringifierToMark = this;
201
202 if (!m_replacer.isObject())
203 return;
204
205 if (asObject(value: m_replacer)->inherits(info: &JSArray::info)) {
206 m_usingArrayReplacer = true;
207 JSObject* array = asObject(value: m_replacer);
208 unsigned length = array->get(exec, propertyName: exec->globalData().propertyNames->length).toUInt32(exec);
209 for (unsigned i = 0; i < length; ++i) {
210 JSValue name = array->get(exec, propertyName: i);
211 if (exec->hadException())
212 break;
213
214 UString propertyName;
215 if (name.getString(exec, s&: propertyName)) {
216 m_arrayReplacerPropertyNames.add(identifier: Identifier(exec, propertyName));
217 continue;
218 }
219
220 double value = 0;
221 if (name.getNumber(result&: value)) {
222 m_arrayReplacerPropertyNames.add(identifier: Identifier::from(exec, y: value));
223 continue;
224 }
225
226 if (name.isObject()) {
227 if (!asObject(value: name)->inherits(info: &NumberObject::info) && !asObject(value: name)->inherits(info: &StringObject::info))
228 continue;
229 propertyName = name.toString(exec);
230 if (exec->hadException())
231 break;
232 m_arrayReplacerPropertyNames.add(identifier: Identifier(exec, propertyName));
233 }
234 }
235 return;
236 }
237
238 m_replacerCallType = asObject(value: m_replacer)->getCallData(m_replacerCallData);
239}
240
241Stringifier::~Stringifier()
242{
243 ASSERT(m_exec->globalData().firstStringifierToMark == this);
244 m_exec->globalData().firstStringifierToMark = m_nextStringifierToMark;
245}
246
247void Stringifier::markAggregate(MarkStack& markStack)
248{
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(cell: m_holderStack[i].object());
253 }
254}
255
256JSValue Stringifier::stringify(JSValue value)
257{
258 JSObject* object = constructEmptyObject(exec: m_exec);
259 if (m_exec->hadException())
260 return jsNull();
261
262 PropertyNameForFunctionCall emptyPropertyName(m_exec->globalData().propertyNames->emptyIdentifier);
263 object->putDirect(propertyName: m_exec->globalData().propertyNames->emptyIdentifier, value);
264
265 StringBuilder result;
266 if (appendStringifiedValue(result, value, holder: object, emptyPropertyName) != StringifySucceeded)
267 return jsUndefined();
268 if (m_exec->hadException())
269 return jsNull();
270
271 return jsString(exec: m_exec, s: result.release());
272}
273
274void Stringifier::appendQuotedString(StringBuilder& builder, const UString& value)
275{
276 int length = value.size();
277
278 // String length plus 2 for quote marks plus 8 so we can accomodate a few escaped characters.
279 builder.reserveCapacity(newCapacity: builder.size() + length + 2 + 8);
280
281 builder.append(u: '"');
282
283 const UChar* data = value.data();
284 for (int i = 0; i < length; ++i) {
285 int start = i;
286 while (i < length && (data[i] > 0x1F && data[i] != '"' && data[i] != '\\'))
287 ++i;
288 builder.append(str: data + start, len: i - start);
289 if (i >= length)
290 break;
291 switch (data[i]) {
292 case '\t':
293 builder.append(u: '\\');
294 builder.append(u: 't');
295 break;
296 case '\r':
297 builder.append(u: '\\');
298 builder.append(u: 'r');
299 break;
300 case '\n':
301 builder.append(u: '\\');
302 builder.append(u: 'n');
303 break;
304 case '\f':
305 builder.append(u: '\\');
306 builder.append(u: 'f');
307 break;
308 case '\b':
309 builder.append(u: '\\');
310 builder.append(u: 'b');
311 break;
312 case '"':
313 builder.append(u: '\\');
314 builder.append(u: '"');
315 break;
316 case '\\':
317 builder.append(u: '\\');
318 builder.append(u: '\\');
319 break;
320 default:
321 static const char hexDigits[] = "0123456789abcdef";
322 UChar ch = data[i];
323 UChar hex[] = { '\\', 'u',
324 static_cast<UChar>(hexDigits[(ch >> 12) & 0xF]),
325 static_cast<UChar>(hexDigits[(ch >> 8) & 0xF]),
326 static_cast<UChar>(hexDigits[(ch >> 4) & 0xF]),
327 static_cast<UChar>(hexDigits[ch & 0xF]) };
328 builder.append(str: hex, len: sizeof(hex) / sizeof(UChar));
329 break;
330 }
331 }
332
333 builder.append(u: '"');
334}
335
336inline JSValue Stringifier::toJSON(JSValue value, const PropertyNameForFunctionCall& propertyName)
337{
338 ASSERT(!m_exec->hadException());
339 if (!value.isObject() || !asObject(value)->hasProperty(m_exec, propertyName: m_exec->globalData().propertyNames->toJSON))
340 return value;
341
342 JSValue toJSONFunction = asObject(value)->get(exec: m_exec, propertyName: m_exec->globalData().propertyNames->toJSON);
343 if (m_exec->hadException())
344 return jsNull();
345
346 if (!toJSONFunction.isObject())
347 return value;
348
349 JSObject* object = asObject(value: toJSONFunction);
350 CallData callData;
351 CallType callType = object->getCallData(callData);
352 if (callType == CallTypeNone)
353 return value;
354
355 JSValue list[] = { propertyName.value(exec: m_exec) };
356 ArgList args(list, sizeof(list) / sizeof(JSValue));
357 return call(m_exec, functionObject: object, callType, callData, thisValue: value, args);
358}
359
360Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, JSObject* holder, const PropertyNameForFunctionCall& propertyName)
361{
362 // Call the toJSON function.
363 value = toJSON(value, propertyName);
364 if (m_exec->hadException())
365 return StringifyFailed;
366
367 // Call the replacer function.
368 if (m_replacerCallType != CallTypeNone) {
369 JSValue list[] = { propertyName.value(exec: m_exec), value };
370 ArgList args(list, sizeof(list) / sizeof(JSValue));
371 value = call(m_exec, functionObject: m_replacer, m_replacerCallType, m_replacerCallData, thisValue: holder, args);
372 if (m_exec->hadException())
373 return StringifyFailed;
374 }
375
376 if (value.isUndefined() && !holder->inherits(info: &JSArray::info))
377 return StringifyFailedDueToUndefinedValue;
378
379 if (value.isNull()) {
380 builder.append(str: "null");
381 return StringifySucceeded;
382 }
383
384 value = unwrapBoxedPrimitive(exec: m_exec, value);
385
386 if (m_exec->hadException())
387 return StringifyFailed;
388
389 if (value.isBoolean()) {
390 builder.append(str: value.getBoolean() ? "true" : "false");
391 return StringifySucceeded;
392 }
393
394 UString stringValue;
395 if (value.getString(exec: m_exec, s&: stringValue)) {
396 appendQuotedString(builder, value: stringValue);
397 return StringifySucceeded;
398 }
399
400 double numericValue;
401 if (value.getNumber(result&: numericValue)) {
402 if (!std::isfinite(x: numericValue))
403 builder.append(str: "null");
404 else
405 builder.append(str: UString::from(numericValue));
406 return StringifySucceeded;
407 }
408
409 if (!value.isObject())
410 return StringifyFailed;
411
412 JSObject* object = asObject(value);
413
414 CallData callData;
415 if (object->getCallData(callData) != CallTypeNone) {
416 if (holder->inherits(info: &JSArray::info)) {
417 builder.append(str: "null");
418 return StringifySucceeded;
419 }
420 return StringifyFailedDueToUndefinedValue;
421 }
422
423 // Handle cycle detection, and put the holder on the stack.
424 if (!m_holderCycleDetector.add(value: object).second) {
425 throwError(m_exec, TypeError, message: "JSON.stringify cannot serialize cyclic structures.");
426 return StringifyFailed;
427 }
428 bool holderStackWasEmpty = m_holderStack.isEmpty();
429 m_holderStack.append(val: object);
430 if (!holderStackWasEmpty)
431 return StringifySucceeded;
432
433 // If this is the outermost call, then loop to handle everything on the holder stack.
434 //TimeoutChecker localTimeoutChecker(*m_exec->globalData().timeoutChecker);
435 TimeoutChecker localTimeoutChecker;
436 localTimeoutChecker.copyTimeoutValues(other: m_exec->globalData().timeoutChecker);
437 localTimeoutChecker.reset();
438 unsigned tickCount = localTimeoutChecker.ticksUntilNextCheck();
439 do {
440 while (m_holderStack.last().appendNextProperty(*this, builder)) {
441 if (m_exec->hadException())
442 return StringifyFailed;
443 if (!--tickCount) {
444 if (localTimeoutChecker.didTimeOut(m_exec)) {
445 m_exec->setException(createInterruptedExecutionException(&m_exec->globalData()));
446 return StringifyFailed;
447 }
448 tickCount = localTimeoutChecker.ticksUntilNextCheck();
449 }
450 }
451 m_holderCycleDetector.remove(value: m_holderStack.last().object());
452 m_holderStack.removeLast();
453 } while (!m_holderStack.isEmpty());
454 return StringifySucceeded;
455}
456
457inline bool Stringifier::willIndent() const
458{
459 return !m_gap.isEmpty();
460}
461
462inline void Stringifier::indent()
463{
464 // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent.
465 int newSize = m_indent.size() + m_gap.size();
466 if (newSize > m_repeatedGap.size())
467 m_repeatedGap = makeString(string1: m_repeatedGap, string2: m_gap);
468 ASSERT(newSize <= m_repeatedGap.size());
469 m_indent = m_repeatedGap.substr(pos: 0, len: newSize);
470}
471
472inline void Stringifier::unindent()
473{
474 ASSERT(m_indent.size() >= m_gap.size());
475 m_indent = m_repeatedGap.substr(pos: 0, len: m_indent.size() - m_gap.size());
476}
477
478inline void Stringifier::startNewLine(StringBuilder& builder) const
479{
480 if (m_gap.isEmpty())
481 return;
482 builder.append(u: '\n');
483 builder.append(str: m_indent);
484}
485
486inline Stringifier::Holder::Holder(JSObject* object)
487 : m_object(object)
488 , m_isArray(object->inherits(info: &JSArray::info))
489 , m_index(0)
490{
491}
492
493bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBuilder& builder)
494{
495 ASSERT(m_index <= m_size);
496
497 ExecState* exec = stringifier.m_exec;
498
499 // First time through, initialize.
500 if (!m_index) {
501 if (m_isArray) {
502 m_isJSArray = isJSArray(globalData: &exec->globalData(), cell: m_object);
503 m_size = m_object->get(exec, propertyName: exec->globalData().propertyNames->length).toUInt32(exec);
504 builder.append(u: '[');
505 } else {
506 if (stringifier.m_usingArrayReplacer)
507 m_propertyNames = stringifier.m_arrayReplacerPropertyNames.data();
508 else {
509 PropertyNameArray objectPropertyNames(exec);
510 m_object->getOwnPropertyNames(exec, objectPropertyNames);
511 m_propertyNames = objectPropertyNames.releaseData();
512 }
513 m_size = m_propertyNames->propertyNameVector().size();
514 builder.append(u: '{');
515 }
516 stringifier.indent();
517 }
518
519 // Last time through, finish up and return false.
520 if (m_index == m_size) {
521 stringifier.unindent();
522 if (m_size && builder[builder.size() - 1] != '{')
523 stringifier.startNewLine(builder);
524 builder.append(u: m_isArray ? ']' : '}');
525 return false;
526 }
527
528 // Handle a single element of the array or object.
529 unsigned index = m_index++;
530 unsigned rollBackPoint = 0;
531 StringifyResult stringifyResult;
532 if (m_isArray) {
533 // Get the value.
534 JSValue value;
535 if (m_isJSArray && asArray(cell: m_object)->canGetIndex(i: index))
536 value = asArray(cell: m_object)->getIndex(i: index);
537 else {
538 PropertySlot slot(m_object);
539 if (!m_object->getOwnPropertySlot(exec, propertyName: index, slot))
540 slot.setUndefined();
541 if (exec->hadException())
542 return false;
543 value = slot.getValue(exec, propertyName: index);
544 }
545
546 // Append the separator string.
547 if (index)
548 builder.append(u: ',');
549 stringifier.startNewLine(builder);
550
551 // Append the stringified value.
552 stringifyResult = stringifier.appendStringifiedValue(builder, value, holder: m_object, propertyName: index);
553 } else {
554 // Get the value.
555 PropertySlot slot(m_object);
556 Identifier& propertyName = m_propertyNames->propertyNameVector()[index];
557 if (!m_object->getOwnPropertySlot(exec, propertyName, slot))
558 return true;
559 JSValue value = slot.getValue(exec, propertyName);
560 if (exec->hadException())
561 return false;
562
563 rollBackPoint = builder.size();
564
565 // Append the separator string.
566 if (builder[rollBackPoint - 1] != '{')
567 builder.append(u: ',');
568 stringifier.startNewLine(builder);
569
570 // Append the property name.
571 appendQuotedString(builder, value: propertyName.ustring());
572 builder.append(u: ':');
573 if (stringifier.willIndent())
574 builder.append(u: ' ');
575
576 // Append the stringified value.
577 stringifyResult = stringifier.appendStringifiedValue(builder, value, holder: m_object, propertyName);
578 }
579
580 // From this point on, no access to the this pointer or to any members, because the
581 // Holder object may have moved if the call to stringify pushed a new Holder onto
582 // m_holderStack.
583
584 switch (stringifyResult) {
585 case StringifyFailed:
586 builder.append(str: "null");
587 break;
588 case StringifySucceeded:
589 break;
590 case StringifyFailedDueToUndefinedValue:
591 // This only occurs when get an undefined value for an object property.
592 // In this case we don't want the separator and property name that we
593 // already appended, so roll back.
594 builder.resize(size: rollBackPoint);
595 break;
596 }
597
598 return true;
599}
600
601// ------------------------------ JSONObject --------------------------------
602
603const ClassInfo JSONObject::info = { .className: "JSON", .parentClass: 0, .staticPropHashTable: 0, .classPropHashTableGetterFunction: ExecState::jsonTable };
604
605/* Source for JSONObject.lut.h
606@begin jsonTable
607 parse JSONProtoFuncParse DontEnum|Function 1
608 stringify JSONProtoFuncStringify DontEnum|Function 1
609@end
610*/
611
612// ECMA 15.8
613
614bool JSONObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
615{
616 return getStaticFunctionSlot<JSObject>(exec, table: ExecState::jsonTable(callFrame: exec), thisObj: this, propertyName, slot);
617}
618
619bool JSONObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
620{
621 return getStaticFunctionDescriptor<JSObject>(exec, table: ExecState::jsonTable(callFrame: exec), thisObj: this, propertyName, descriptor);
622}
623
624void JSONObject::markStringifiers(MarkStack& markStack, Stringifier* stringifier)
625{
626 stringifier->markAggregate(markStack);
627}
628
629class Walker {
630public:
631 Walker(ExecState* exec, JSObject* function, CallType callType, CallData callData)
632 : m_exec(exec)
633 , m_function(function)
634 , m_callType(callType)
635 , m_callData(callData)
636 {
637 }
638 JSValue walk(JSValue unfiltered);
639private:
640 JSValue callReviver(JSObject* thisObj, JSValue property, JSValue unfiltered)
641 {
642 JSValue args[] = { property, unfiltered };
643 ArgList argList(args, 2);
644 return call(m_exec, functionObject: m_function, m_callType, m_callData, thisValue: thisObj, argList);
645 }
646
647 friend class Holder;
648
649 ExecState* m_exec;
650 JSObject* m_function;
651 CallType m_callType;
652 CallData m_callData;
653};
654
655// We clamp recursion well beyond anything reasonable, but we also have a timeout check
656// to guard against "infinite" execution by inserting arbitrarily large objects.
657static const unsigned maximumFilterRecursion = 40000;
658enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember,
659 ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember };
660NEVER_INLINE JSValue Walker::walk(JSValue unfiltered)
661{
662 Vector<PropertyNameArray, 16> propertyStack;
663 Vector<uint32_t, 16> indexStack;
664 Vector<JSObject*, 16> objectStack;
665 Vector<JSArray*, 16> arrayStack;
666
667 Vector<WalkerState, 16> stateStack;
668 WalkerState state = StateUnknown;
669 JSValue inValue = unfiltered;
670 JSValue outValue = jsNull();
671
672 TimeoutChecker localTimeoutChecker;
673 localTimeoutChecker.copyTimeoutValues(other: m_exec->globalData().timeoutChecker);
674 localTimeoutChecker.reset();
675 unsigned tickCount = localTimeoutChecker.ticksUntilNextCheck();
676 while (1) {
677 switch (state) {
678 arrayStartState:
679 case ArrayStartState: {
680 ASSERT(inValue.isObject());
681 ASSERT(isJSArray(&m_exec->globalData(), asObject(inValue)) || asObject(inValue)->inherits(&JSArray::info));
682 if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) {
683 m_exec->setException(createStackOverflowError(m_exec));
684 return jsUndefined();
685 }
686
687 JSArray* array = asArray(value: inValue);
688 arrayStack.append(val: array);
689 indexStack.append(val: 0);
690 // fallthrough
691 }
692 arrayStartVisitMember:
693 case ArrayStartVisitMember: {
694 if (!--tickCount) {
695 if (localTimeoutChecker.didTimeOut(m_exec)) {
696 m_exec->setException(createInterruptedExecutionException(&m_exec->globalData()));
697 return jsUndefined();
698 }
699 tickCount = localTimeoutChecker.ticksUntilNextCheck();
700 }
701
702 JSArray* array = arrayStack.last();
703 uint32_t index = indexStack.last();
704 if (index == array->length()) {
705 outValue = array;
706 arrayStack.removeLast();
707 indexStack.removeLast();
708 break;
709 }
710 if (isJSArray(globalData: &m_exec->globalData(), cell: array) && array->canGetIndex(i: index))
711 inValue = array->getIndex(i: index);
712 else {
713 PropertySlot slot;
714 if (array->getOwnPropertySlot(m_exec, propertyName: index, slot))
715 inValue = slot.getValue(exec: m_exec, propertyName: index);
716 else
717 inValue = jsUndefined();
718 }
719
720 if (inValue.isObject()) {
721 stateStack.append(val: ArrayEndVisitMember);
722 goto stateUnknown;
723 } else
724 outValue = inValue;
725 // fallthrough
726 }
727 case ArrayEndVisitMember: {
728 JSArray* array = arrayStack.last();
729 JSValue filteredValue = callReviver(thisObj: array, property: jsString(exec: m_exec, s: UString::from(indexStack.last())), unfiltered: outValue);
730 if (filteredValue.isUndefined())
731 array->deleteProperty(m_exec, propertyName: indexStack.last());
732 else {
733 if (isJSArray(globalData: &m_exec->globalData(), cell: array) && array->canSetIndex(i: indexStack.last()))
734 array->setIndex(i: indexStack.last(), v: filteredValue);
735 else
736 array->put(m_exec, propertyName: indexStack.last(), filteredValue);
737 }
738 if (m_exec->hadException())
739 return jsNull();
740 indexStack.last()++;
741 goto arrayStartVisitMember;
742 }
743 objectStartState:
744 case ObjectStartState: {
745 ASSERT(inValue.isObject());
746 ASSERT(!isJSArray(&m_exec->globalData(), asObject(inValue)) && !asObject(inValue)->inherits(&JSArray::info));
747 if (objectStack.size() + arrayStack.size() > maximumFilterRecursion) {
748 m_exec->setException(createStackOverflowError(m_exec));
749 return jsUndefined();
750 }
751
752 JSObject* object = asObject(value: inValue);
753 objectStack.append(val: object);
754 indexStack.append(val: 0);
755 propertyStack.append(val: PropertyNameArray(m_exec));
756 object->getOwnPropertyNames(m_exec, propertyStack.last());
757 // fallthrough
758 }
759 objectStartVisitMember:
760 case ObjectStartVisitMember: {
761 if (!--tickCount) {
762 if (localTimeoutChecker.didTimeOut(m_exec)) {
763 m_exec->setException(createInterruptedExecutionException(&m_exec->globalData()));
764 return jsUndefined();
765 }
766 tickCount = localTimeoutChecker.ticksUntilNextCheck();
767 }
768
769 JSObject* object = objectStack.last();
770 uint32_t index = indexStack.last();
771 PropertyNameArray& properties = propertyStack.last();
772 if (index == properties.size()) {
773 outValue = object;
774 objectStack.removeLast();
775 indexStack.removeLast();
776 propertyStack.removeLast();
777 break;
778 }
779 PropertySlot slot;
780 if (object->getOwnPropertySlot(exec: m_exec, propertyName: properties[index], slot))
781 inValue = slot.getValue(exec: m_exec, propertyName: properties[index]);
782 else
783 inValue = jsUndefined();
784
785 // The holder may be modified by the reviver function so any lookup may throw
786 if (m_exec->hadException())
787 return jsNull();
788
789 if (inValue.isObject()) {
790 stateStack.append(val: ObjectEndVisitMember);
791 goto stateUnknown;
792 } else
793 outValue = inValue;
794 // fallthrough
795 }
796 case ObjectEndVisitMember: {
797 JSObject* object = objectStack.last();
798 Identifier prop = propertyStack.last()[indexStack.last()];
799 PutPropertySlot slot;
800 JSValue filteredValue = callReviver(thisObj: object, property: jsString(exec: m_exec, s: prop.ustring()), unfiltered: outValue);
801 if (filteredValue.isUndefined())
802 object->deleteProperty(m_exec, propertyName: prop);
803 else
804 object->put(m_exec, propertyName: prop, value: filteredValue, slot);
805 if (m_exec->hadException())
806 return jsNull();
807 indexStack.last()++;
808 goto objectStartVisitMember;
809 }
810 stateUnknown:
811 case StateUnknown:
812 if (!inValue.isObject()) {
813 outValue = inValue;
814 break;
815 }
816 JSObject* object = asObject(value: inValue);
817 if (isJSArray(globalData: &m_exec->globalData(), cell: object) || object->inherits(info: &JSArray::info))
818 goto arrayStartState;
819 goto objectStartState;
820 }
821 if (stateStack.isEmpty())
822 break;
823
824 state = stateStack.last();
825 stateStack.removeLast();
826
827 if (!--tickCount) {
828 if (localTimeoutChecker.didTimeOut(m_exec)) {
829 m_exec->setException(createInterruptedExecutionException(&m_exec->globalData()));
830 return jsUndefined();
831 }
832 tickCount = localTimeoutChecker.ticksUntilNextCheck();
833 }
834 }
835 JSObject* finalHolder = constructEmptyObject(exec: m_exec);
836 PutPropertySlot slot;
837 finalHolder->put(m_exec, propertyName: m_exec->globalData().propertyNames->emptyIdentifier, value: outValue, slot);
838 return callReviver(thisObj: finalHolder, property: jsEmptyString(exec: m_exec), unfiltered: outValue);
839}
840
841// ECMA-262 v5 15.12.2
842JSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec, JSObject*, JSValue, const ArgList& args)
843{
844 if (args.isEmpty())
845 return throwError(exec, GeneralError, message: "JSON.parse requires at least one parameter");
846 JSValue value = args.at(idx: 0);
847 UString source = value.toString(exec);
848 if (exec->hadException())
849 return jsNull();
850
851 LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON);
852 JSValue unfiltered = jsonParser.tryLiteralParse();
853 if (!unfiltered)
854 return throwError(exec, SyntaxError, message: "Unable to parse JSON string");
855
856 if (args.size() < 2)
857 return unfiltered;
858
859 JSValue function = args.at(idx: 1);
860 CallData callData;
861 CallType callType = function.getCallData(callData);
862 if (callType == CallTypeNone)
863 return unfiltered;
864 return Walker(exec, asObject(value: function), callType, callData).walk(unfiltered);
865}
866
867// ECMA-262 v5 15.12.3
868JSValue JSC_HOST_CALL JSONProtoFuncStringify(ExecState* exec, JSObject*, JSValue, const ArgList& args)
869{
870 if (args.isEmpty())
871 return throwError(exec, GeneralError, message: "No input to stringify");
872 JSValue value = args.at(idx: 0);
873 JSValue replacer = args.at(idx: 1);
874 JSValue space = args.at(idx: 2);
875 return Stringifier(exec, replacer, space).stringify(value);
876}
877
878} // namespace JSC
879

source code of qtscript/src/3rdparty/javascriptcore/JavaScriptCore/runtime/JSONObject.cpp