1/*
2 * Copyright (C) 2009-2019 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 "ArrayConstructor.h"
30#include "BigIntObject.h"
31#include "BooleanObject.h"
32#include "Error.h"
33#include "ExceptionHelpers.h"
34#include "JSArray.h"
35#include "JSArrayInlines.h"
36#include "JSGlobalObject.h"
37#include "LiteralParser.h"
38#include "Lookup.h"
39#include "ObjectConstructor.h"
40#include "JSCInlines.h"
41#include "PropertyNameArray.h"
42#include <wtf/MathExtras.h>
43#include <wtf/text/StringBuilder.h>
44
45namespace JSC {
46
47STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSONObject);
48
49EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(JSGlobalObject*, CallFrame*);
50EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(JSGlobalObject*, CallFrame*);
51
52}
53
54#include "JSONObject.lut.h"
55
56namespace JSC {
57
58JSONObject::JSONObject(VM& vm, Structure* structure)
59 : JSNonFinalObject(vm, structure)
60{
61}
62
63void JSONObject::finishCreation(VM& vm)
64{
65 Base::finishCreation(vm);
66 ASSERT(inherits(vm, info()));
67
68 putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, "JSON"), PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
69}
70
71// PropertyNameForFunctionCall objects must be on the stack, since the JSValue that they create is not marked.
72class PropertyNameForFunctionCall {
73public:
74 PropertyNameForFunctionCall(const Identifier&);
75 PropertyNameForFunctionCall(unsigned);
76
77 JSValue value(JSGlobalObject*) const;
78
79private:
80 const Identifier* m_identifier;
81 unsigned m_number;
82 mutable JSValue m_value;
83};
84
85class Stringifier {
86 WTF_MAKE_NONCOPYABLE(Stringifier);
87 WTF_FORBID_HEAP_ALLOCATION;
88public:
89 Stringifier(JSGlobalObject*, JSValue replacer, JSValue space);
90 JSValue stringify(JSValue);
91
92private:
93 class Holder {
94 public:
95 enum RootHolderTag { RootHolder };
96 Holder(JSGlobalObject*, JSObject*);
97 Holder(RootHolderTag, JSObject*);
98
99 JSObject* object() const { return m_object; }
100 bool isArray() const { return m_isArray; }
101
102 bool appendNextProperty(Stringifier&, StringBuilder&);
103
104 private:
105 JSObject* m_object;
106 const bool m_isJSArray;
107 const bool m_isArray;
108 unsigned m_index { 0 };
109 unsigned m_size { 0 };
110 RefPtr<PropertyNameArrayData> m_propertyNames;
111 };
112
113 friend class Holder;
114
115 JSValue toJSON(JSValue, const PropertyNameForFunctionCall&);
116 JSValue toJSONImpl(VM&, JSValue, JSValue toJSONFunction, const PropertyNameForFunctionCall&);
117
118 enum StringifyResult { StringifyFailed, StringifySucceeded, StringifyFailedDueToUndefinedOrSymbolValue };
119 StringifyResult appendStringifiedValue(StringBuilder&, JSValue, const Holder&, const PropertyNameForFunctionCall&);
120
121 bool willIndent() const;
122 void indent();
123 void unindent();
124 void startNewLine(StringBuilder&) const;
125 bool isCallableReplacer() const { return m_replacerCallType != CallType::None; }
126
127 JSGlobalObject* const m_globalObject;
128 JSValue m_replacer;
129 bool m_usingArrayReplacer { false };
130 PropertyNameArray m_arrayReplacerPropertyNames;
131 CallType m_replacerCallType { CallType::None };
132 CallData m_replacerCallData;
133 String m_gap;
134
135 MarkedArgumentBuffer m_objectStack;
136 Vector<Holder, 16, UnsafeVectorOverflow> m_holderStack;
137 String m_repeatedGap;
138 String m_indent;
139};
140
141// ------------------------------ helper functions --------------------------------
142
143static inline JSValue unwrapBoxedPrimitive(JSGlobalObject* globalObject, JSValue value)
144{
145 VM& vm = globalObject->vm();
146 if (!value.isObject())
147 return value;
148 JSObject* object = asObject(value);
149 if (object->inherits<NumberObject>(vm))
150 return jsNumber(object->toNumber(globalObject));
151 if (object->inherits<StringObject>(vm))
152 return object->toString(globalObject);
153 if (object->inherits<BooleanObject>(vm) || object->inherits<BigIntObject>(vm))
154 return jsCast<JSWrapperObject*>(object)->internalValue();
155
156 // Do not unwrap SymbolObject to Symbol. It is not performed in the spec.
157 // http://www.ecma-international.org/ecma-262/6.0/#sec-serializejsonproperty
158 return value;
159}
160
161static inline String gap(JSGlobalObject* globalObject, JSValue space)
162{
163 VM& vm = globalObject->vm();
164 auto scope = DECLARE_THROW_SCOPE(vm);
165
166 const unsigned maxGapLength = 10;
167 space = unwrapBoxedPrimitive(globalObject, space);
168 RETURN_IF_EXCEPTION(scope, { });
169
170 // If the space value is a number, create a gap string with that number of spaces.
171 if (space.isNumber()) {
172 double spaceCount = space.asNumber();
173 int count;
174 if (spaceCount > maxGapLength)
175 count = maxGapLength;
176 else if (!(spaceCount > 0))
177 count = 0;
178 else
179 count = static_cast<int>(spaceCount);
180 char spaces[maxGapLength];
181 for (int i = 0; i < count; ++i)
182 spaces[i] = ' ';
183 return String(spaces, count);
184 }
185
186 // If the space value is a string, use it as the gap string, otherwise use no gap string.
187 String spaces = space.getString(globalObject);
188 if (spaces.length() <= maxGapLength)
189 return spaces;
190 return spaces.substringSharingImpl(0, maxGapLength);
191}
192
193// ------------------------------ PropertyNameForFunctionCall --------------------------------
194
195inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(const Identifier& identifier)
196 : m_identifier(&identifier)
197{
198}
199
200inline PropertyNameForFunctionCall::PropertyNameForFunctionCall(unsigned number)
201 : m_identifier(0)
202 , m_number(number)
203{
204}
205
206JSValue PropertyNameForFunctionCall::value(JSGlobalObject* globalObject) const
207{
208 if (!m_value) {
209 VM& vm = globalObject->vm();
210 if (m_identifier)
211 m_value = jsString(vm, m_identifier->string());
212 else {
213 if (m_number <= 9)
214 return vm.smallStrings.singleCharacterString(m_number + '0');
215 m_value = jsNontrivialString(vm, vm.numericStrings.add(m_number));
216 }
217 }
218 return m_value;
219}
220
221// ------------------------------ Stringifier --------------------------------
222
223Stringifier::Stringifier(JSGlobalObject* globalObject, JSValue replacer, JSValue space)
224 : m_globalObject(globalObject)
225 , m_replacer(replacer)
226 , m_arrayReplacerPropertyNames(globalObject->vm(), PropertyNameMode::Strings, PrivateSymbolMode::Exclude)
227{
228 VM& vm = globalObject->vm();
229 auto scope = DECLARE_THROW_SCOPE(vm);
230
231 if (m_replacer.isObject()) {
232 JSObject* replacerObject = asObject(m_replacer);
233
234 m_replacerCallType = CallType::None;
235 if (!replacerObject->isCallable(vm, m_replacerCallType, m_replacerCallData)) {
236 bool isArrayReplacer = JSC::isArray(globalObject, replacerObject);
237 RETURN_IF_EXCEPTION(scope, );
238 if (isArrayReplacer) {
239 m_usingArrayReplacer = true;
240 unsigned length = toLength(globalObject, replacerObject);
241 RETURN_IF_EXCEPTION(scope, );
242 for (unsigned index = 0; index < length; ++index) {
243 JSValue name;
244 if (isJSArray(replacerObject) && replacerObject->canGetIndexQuickly(index))
245 name = replacerObject->getIndexQuickly(index);
246 else {
247 name = replacerObject->get(globalObject, index);
248 RETURN_IF_EXCEPTION(scope, );
249 }
250 if (name.isObject()) {
251 auto* nameObject = jsCast<JSObject*>(name);
252 if (!nameObject->inherits<NumberObject>(vm) && !nameObject->inherits<StringObject>(vm))
253 continue;
254 } else if (!name.isNumber() && !name.isString())
255 continue;
256 JSString* propertyNameString = name.toString(globalObject);
257 RETURN_IF_EXCEPTION(scope, );
258 auto propertyName = propertyNameString->toIdentifier(globalObject);
259 RETURN_IF_EXCEPTION(scope, );
260 m_arrayReplacerPropertyNames.add(WTFMove(propertyName));
261 }
262 }
263 }
264 }
265
266 scope.release();
267 m_gap = gap(globalObject, space);
268}
269
270JSValue Stringifier::stringify(JSValue value)
271{
272 VM& vm = m_globalObject->vm();
273 auto scope = DECLARE_THROW_SCOPE(vm);
274
275 PropertyNameForFunctionCall emptyPropertyName(vm.propertyNames->emptyIdentifier);
276
277 // If the replacer is not callable, root object wrapper is non-user-observable.
278 // We can skip creating this wrapper object.
279 JSObject* object = nullptr;
280 if (isCallableReplacer()) {
281 object = constructEmptyObject(m_globalObject);
282 RETURN_IF_EXCEPTION(scope, jsUndefined());
283 object->putDirect(vm, vm.propertyNames->emptyIdentifier, value);
284 }
285
286 StringBuilder result(StringBuilder::OverflowHandler::RecordOverflow);
287 Holder root(Holder::RootHolder, object);
288 auto stringifyResult = appendStringifiedValue(result, value, root, emptyPropertyName);
289 RETURN_IF_EXCEPTION(scope, jsUndefined());
290 if (UNLIKELY(result.hasOverflowed())) {
291 throwOutOfMemoryError(m_globalObject, scope);
292 return jsUndefined();
293 }
294 if (UNLIKELY(stringifyResult != StringifySucceeded))
295 return jsUndefined();
296 RELEASE_AND_RETURN(scope, jsString(vm, result.toString()));
297}
298
299ALWAYS_INLINE JSValue Stringifier::toJSON(JSValue baseValue, const PropertyNameForFunctionCall& propertyName)
300{
301 VM& vm = m_globalObject->vm();
302 auto scope = DECLARE_THROW_SCOPE(vm);
303 scope.assertNoException();
304
305 PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get);
306 bool hasProperty = baseValue.getPropertySlot(m_globalObject, vm.propertyNames->toJSON, slot);
307 EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
308 if (!hasProperty)
309 return baseValue;
310
311 JSValue toJSONFunction = slot.getValue(m_globalObject, vm.propertyNames->toJSON);
312 RETURN_IF_EXCEPTION(scope, { });
313 RELEASE_AND_RETURN(scope, toJSONImpl(vm, baseValue, toJSONFunction, propertyName));
314}
315
316JSValue Stringifier::toJSONImpl(VM& vm, JSValue baseValue, JSValue toJSONFunction, const PropertyNameForFunctionCall& propertyName)
317{
318 CallType callType;
319 CallData callData;
320 if (!toJSONFunction.isCallable(vm, callType, callData))
321 return baseValue;
322
323 MarkedArgumentBuffer args;
324 args.append(propertyName.value(m_globalObject));
325 ASSERT(!args.hasOverflowed());
326 return call(m_globalObject, asObject(toJSONFunction), callType, callData, baseValue, args);
327}
328
329Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& builder, JSValue value, const Holder& holder, const PropertyNameForFunctionCall& propertyName)
330{
331 VM& vm = m_globalObject->vm();
332 auto scope = DECLARE_THROW_SCOPE(vm);
333
334 // Call the toJSON function.
335 if (value.isObject() || value.isBigInt()) {
336 value = toJSON(value, propertyName);
337 RETURN_IF_EXCEPTION(scope, StringifyFailed);
338 }
339
340 // Call the replacer function.
341 if (isCallableReplacer()) {
342 MarkedArgumentBuffer args;
343 args.append(propertyName.value(m_globalObject));
344 args.append(value);
345 ASSERT(!args.hasOverflowed());
346 ASSERT(holder.object());
347 value = call(m_globalObject, m_replacer, m_replacerCallType, m_replacerCallData, holder.object(), args);
348 RETURN_IF_EXCEPTION(scope, StringifyFailed);
349 }
350
351 if ((value.isUndefined() || value.isSymbol()) && !holder.isArray())
352 return StringifyFailedDueToUndefinedOrSymbolValue;
353
354 if (value.isNull()) {
355 builder.appendLiteral("null");
356 return StringifySucceeded;
357 }
358
359 value = unwrapBoxedPrimitive(m_globalObject, value);
360
361 RETURN_IF_EXCEPTION(scope, StringifyFailed);
362
363 if (value.isBoolean()) {
364 if (value.isTrue())
365 builder.appendLiteral("true");
366 else
367 builder.appendLiteral("false");
368 return StringifySucceeded;
369 }
370
371 if (value.isString()) {
372 const String& string = asString(value)->value(m_globalObject);
373 RETURN_IF_EXCEPTION(scope, StringifyFailed);
374 builder.appendQuotedJSONString(string);
375 return StringifySucceeded;
376 }
377
378 if (value.isNumber()) {
379 if (value.isInt32())
380 builder.appendNumber(value.asInt32());
381 else {
382 double number = value.asNumber();
383 if (!std::isfinite(number))
384 builder.appendLiteral("null");
385 else
386 builder.appendNumber(number);
387 }
388 return StringifySucceeded;
389 }
390
391 if (value.isBigInt()) {
392 throwTypeError(m_globalObject, scope, "JSON.stringify cannot serialize BigInt."_s);
393 return StringifyFailed;
394 }
395
396 if (!value.isObject())
397 return StringifyFailed;
398
399 JSObject* object = asObject(value);
400 if (object->isFunction(vm)) {
401 if (holder.isArray()) {
402 builder.appendLiteral("null");
403 return StringifySucceeded;
404 }
405 return StringifyFailedDueToUndefinedOrSymbolValue;
406 }
407
408 if (UNLIKELY(builder.hasOverflowed()))
409 return StringifyFailed;
410
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 throwTypeError(m_globalObject, scope, "JSON.stringify cannot serialize cyclic structures."_s);
415 return StringifyFailed;
416 }
417 }
418
419 bool holderStackWasEmpty = m_holderStack.isEmpty();
420 m_holderStack.append(Holder(m_globalObject, object));
421 m_objectStack.appendWithCrashOnOverflow(object);
422 RETURN_IF_EXCEPTION(scope, StringifyFailed);
423 if (!holderStackWasEmpty)
424 return StringifySucceeded;
425
426 do {
427 while (m_holderStack.last().appendNextProperty(*this, builder))
428 RETURN_IF_EXCEPTION(scope, StringifyFailed);
429 RETURN_IF_EXCEPTION(scope, StringifyFailed);
430 if (UNLIKELY(builder.hasOverflowed()))
431 return StringifyFailed;
432 m_holderStack.removeLast();
433 m_objectStack.removeLast();
434 } while (!m_holderStack.isEmpty());
435 return StringifySucceeded;
436}
437
438inline bool Stringifier::willIndent() const
439{
440 return !m_gap.isEmpty();
441}
442
443inline void Stringifier::indent()
444{
445 // Use a single shared string, m_repeatedGap, so we don't keep allocating new ones as we indent and unindent.
446 unsigned newSize = m_indent.length() + m_gap.length();
447 if (newSize > m_repeatedGap.length())
448 m_repeatedGap = makeString(m_repeatedGap, m_gap);
449 ASSERT(newSize <= m_repeatedGap.length());
450 m_indent = m_repeatedGap.substringSharingImpl(0, newSize);
451}
452
453inline void Stringifier::unindent()
454{
455 ASSERT(m_indent.length() >= m_gap.length());
456 m_indent = m_repeatedGap.substringSharingImpl(0, m_indent.length() - m_gap.length());
457}
458
459inline void Stringifier::startNewLine(StringBuilder& builder) const
460{
461 if (m_gap.isEmpty())
462 return;
463 builder.append('\n');
464 builder.append(m_indent);
465}
466
467inline Stringifier::Holder::Holder(JSGlobalObject* globalObject, JSObject* object)
468 : m_object(object)
469 , m_isJSArray(isJSArray(object))
470 , m_isArray(JSC::isArray(globalObject, object))
471{
472}
473
474inline Stringifier::Holder::Holder(RootHolderTag, JSObject* object)
475 : m_object(object)
476 , m_isJSArray(false)
477 , m_isArray(false)
478{
479}
480
481bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBuilder& builder)
482{
483 ASSERT(m_index <= m_size);
484
485 JSGlobalObject* globalObject = stringifier.m_globalObject;
486 VM& vm = globalObject->vm();
487 auto scope = DECLARE_THROW_SCOPE(vm);
488
489 // First time through, initialize.
490 if (!m_index) {
491 if (m_isArray) {
492 m_size = toLength(globalObject, m_object);
493 RETURN_IF_EXCEPTION(scope, false);
494 builder.append('[');
495 } else {
496 if (stringifier.m_usingArrayReplacer)
497 m_propertyNames = stringifier.m_arrayReplacerPropertyNames.data();
498 else {
499 PropertyNameArray objectPropertyNames(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
500 m_object->methodTable(vm)->getOwnPropertyNames(m_object, globalObject, objectPropertyNames, EnumerationMode());
501 RETURN_IF_EXCEPTION(scope, false);
502 m_propertyNames = objectPropertyNames.releaseData();
503 }
504 m_size = m_propertyNames->propertyNameVector().size();
505 builder.append('{');
506 }
507 stringifier.indent();
508 }
509 if (UNLIKELY(builder.hasOverflowed()))
510 return false;
511
512 // Last time through, finish up and return false.
513 if (m_index == m_size) {
514 stringifier.unindent();
515 if (m_size && builder[builder.length() - 1] != '{')
516 stringifier.startNewLine(builder);
517 builder.append(m_isArray ? ']' : '}');
518 return false;
519 }
520
521 // Handle a single element of the array or object.
522 unsigned index = m_index++;
523 unsigned rollBackPoint = 0;
524 StringifyResult stringifyResult;
525 if (m_isArray) {
526 // Get the value.
527 JSValue value;
528 if (m_isJSArray && m_object->canGetIndexQuickly(index))
529 value = m_object->getIndexQuickly(index);
530 else {
531 PropertySlot slot(m_object, PropertySlot::InternalMethodType::Get);
532 bool hasProperty = m_object->getPropertySlot(globalObject, index, slot);
533 EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
534 if (hasProperty)
535 value = slot.getValue(globalObject, index);
536 else
537 value = jsUndefined();
538 RETURN_IF_EXCEPTION(scope, false);
539 }
540
541 // Append the separator string.
542 if (index)
543 builder.append(',');
544 stringifier.startNewLine(builder);
545
546 // Append the stringified value.
547 stringifyResult = stringifier.appendStringifiedValue(builder, value, *this, index);
548 ASSERT(stringifyResult != StringifyFailedDueToUndefinedOrSymbolValue);
549 } else {
550 // Get the value.
551 PropertySlot slot(m_object, PropertySlot::InternalMethodType::Get);
552 Identifier& propertyName = m_propertyNames->propertyNameVector()[index];
553 bool hasProperty = m_object->getPropertySlot(globalObject, propertyName, slot);
554 EXCEPTION_ASSERT(!scope.exception() || !hasProperty);
555 if (!hasProperty)
556 return true;
557 JSValue value = slot.getValue(globalObject, propertyName);
558 RETURN_IF_EXCEPTION(scope, false);
559
560 rollBackPoint = builder.length();
561
562 // Append the separator string.
563 if (builder[rollBackPoint - 1] != '{')
564 builder.append(',');
565 stringifier.startNewLine(builder);
566
567 // Append the property name.
568 builder.appendQuotedJSONString(propertyName.string());
569 builder.append(':');
570 if (stringifier.willIndent())
571 builder.append(' ');
572
573 // Append the stringified value.
574 stringifyResult = stringifier.appendStringifiedValue(builder, value, *this, propertyName);
575 }
576 RETURN_IF_EXCEPTION(scope, false);
577
578 // From this point on, no access to the this pointer or to any members, because the
579 // Holder object may have moved if the call to stringify pushed a new Holder onto
580 // m_holderStack.
581
582 switch (stringifyResult) {
583 case StringifyFailed:
584 builder.appendLiteral("null");
585 break;
586 case StringifySucceeded:
587 break;
588 case StringifyFailedDueToUndefinedOrSymbolValue:
589 // This only occurs when get an undefined value or a symbol value for
590 // an object property. In this case we don't want the separator and
591 // property name that we already appended, so roll back.
592 builder.resize(rollBackPoint);
593 break;
594 }
595
596 return true;
597}
598
599// ------------------------------ JSONObject --------------------------------
600
601const ClassInfo JSONObject::s_info = { "JSON", &JSNonFinalObject::s_info, &jsonTable, nullptr, CREATE_METHOD_TABLE(JSONObject) };
602
603/* Source for JSONObject.lut.h
604@begin jsonTable
605 parse JSONProtoFuncParse DontEnum|Function 2
606 stringify JSONProtoFuncStringify DontEnum|Function 3
607@end
608*/
609
610// ECMA 15.8
611
612class Walker {
613 WTF_MAKE_NONCOPYABLE(Walker);
614 WTF_FORBID_HEAP_ALLOCATION;
615public:
616 Walker(JSGlobalObject* globalObject, JSObject* function, CallType callType, CallData callData)
617 : m_globalObject(globalObject)
618 , m_function(function)
619 , m_callType(callType)
620 , m_callData(callData)
621 {
622 }
623 JSValue walk(JSValue unfiltered);
624private:
625 JSValue callReviver(JSObject* thisObj, JSValue property, JSValue unfiltered)
626 {
627 MarkedArgumentBuffer args;
628 args.append(property);
629 args.append(unfiltered);
630 ASSERT(!args.hasOverflowed());
631 return call(m_globalObject, m_function, m_callType, m_callData, thisObj, args);
632 }
633
634 friend class Holder;
635
636 JSGlobalObject* m_globalObject;
637 JSObject* m_function;
638 CallType m_callType;
639 CallData m_callData;
640};
641
642// We clamp recursion well beyond anything reasonable.
643static constexpr unsigned maximumFilterRecursion = 40000;
644enum WalkerState { StateUnknown, ArrayStartState, ArrayStartVisitMember, ArrayEndVisitMember,
645 ObjectStartState, ObjectStartVisitMember, ObjectEndVisitMember };
646NEVER_INLINE JSValue Walker::walk(JSValue unfiltered)
647{
648 VM& vm = m_globalObject->vm();
649 auto scope = DECLARE_THROW_SCOPE(vm);
650
651 Vector<PropertyNameArray, 16, UnsafeVectorOverflow> propertyStack;
652 Vector<uint32_t, 16, UnsafeVectorOverflow> indexStack;
653 MarkedArgumentBuffer markedStack;
654 Vector<unsigned, 16, UnsafeVectorOverflow> arrayLengthStack;
655
656 Vector<WalkerState, 16, UnsafeVectorOverflow> stateStack;
657 WalkerState state = StateUnknown;
658 JSValue inValue = unfiltered;
659 JSValue outValue = jsNull();
660
661 while (1) {
662 switch (state) {
663 arrayStartState:
664 case ArrayStartState: {
665 ASSERT(inValue.isObject());
666 ASSERT(isArray(m_globalObject, inValue));
667 EXCEPTION_ASSERT(!scope.exception());
668
669 if (markedStack.size() > maximumFilterRecursion)
670 return throwStackOverflowError(m_globalObject, scope);
671
672 JSObject* array = asObject(inValue);
673 markedStack.appendWithCrashOnOverflow(array);
674 unsigned length = toLength(m_globalObject, array);
675 RETURN_IF_EXCEPTION(scope, { });
676 arrayLengthStack.append(length);
677 indexStack.append(0);
678 }
679 arrayStartVisitMember:
680 FALLTHROUGH;
681 case ArrayStartVisitMember: {
682 JSObject* array = asObject(markedStack.last());
683 uint32_t index = indexStack.last();
684 unsigned arrayLength = arrayLengthStack.last();
685 if (index == arrayLength) {
686 outValue = array;
687 markedStack.removeLast();
688 arrayLengthStack.removeLast();
689 indexStack.removeLast();
690 break;
691 }
692 if (isJSArray(array) && array->canGetIndexQuickly(index))
693 inValue = array->getIndexQuickly(index);
694 else {
695 PropertySlot slot(array, PropertySlot::InternalMethodType::Get);
696 if (array->methodTable(vm)->getOwnPropertySlotByIndex(array, m_globalObject, index, slot))
697 inValue = slot.getValue(m_globalObject, index);
698 else
699 inValue = jsUndefined();
700 RETURN_IF_EXCEPTION(scope, { });
701 }
702
703 if (inValue.isObject()) {
704 stateStack.append(ArrayEndVisitMember);
705 goto stateUnknown;
706 } else
707 outValue = inValue;
708 FALLTHROUGH;
709 }
710 case ArrayEndVisitMember: {
711 JSObject* array = asObject(markedStack.last());
712 JSValue filteredValue = callReviver(array, jsString(vm, String::number(indexStack.last())), outValue);
713 RETURN_IF_EXCEPTION(scope, { });
714 if (filteredValue.isUndefined())
715 array->methodTable(vm)->deletePropertyByIndex(array, m_globalObject, indexStack.last());
716 else
717 array->putDirectIndex(m_globalObject, indexStack.last(), filteredValue, 0, PutDirectIndexShouldNotThrow);
718 RETURN_IF_EXCEPTION(scope, { });
719 indexStack.last()++;
720 goto arrayStartVisitMember;
721 }
722 objectStartState:
723 case ObjectStartState: {
724 ASSERT(inValue.isObject());
725 ASSERT(!isJSArray(inValue));
726 if (markedStack.size() > maximumFilterRecursion)
727 return throwStackOverflowError(m_globalObject, scope);
728
729 JSObject* object = asObject(inValue);
730 markedStack.appendWithCrashOnOverflow(object);
731 indexStack.append(0);
732 propertyStack.append(PropertyNameArray(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude));
733 object->methodTable(vm)->getOwnPropertyNames(object, m_globalObject, propertyStack.last(), EnumerationMode());
734 RETURN_IF_EXCEPTION(scope, { });
735 }
736 objectStartVisitMember:
737 FALLTHROUGH;
738 case ObjectStartVisitMember: {
739 JSObject* object = jsCast<JSObject*>(markedStack.last());
740 uint32_t index = indexStack.last();
741 PropertyNameArray& properties = propertyStack.last();
742 if (index == properties.size()) {
743 outValue = object;
744 markedStack.removeLast();
745 indexStack.removeLast();
746 propertyStack.removeLast();
747 break;
748 }
749 PropertySlot slot(object, PropertySlot::InternalMethodType::Get);
750 if (object->methodTable(vm)->getOwnPropertySlot(object, m_globalObject, properties[index], slot))
751 inValue = slot.getValue(m_globalObject, properties[index]);
752 else
753 inValue = jsUndefined();
754
755 // The holder may be modified by the reviver function so any lookup may throw
756 RETURN_IF_EXCEPTION(scope, { });
757
758 if (inValue.isObject()) {
759 stateStack.append(ObjectEndVisitMember);
760 goto stateUnknown;
761 } else
762 outValue = inValue;
763 FALLTHROUGH;
764 }
765 case ObjectEndVisitMember: {
766 JSObject* object = jsCast<JSObject*>(markedStack.last());
767 Identifier prop = propertyStack.last()[indexStack.last()];
768 PutPropertySlot slot(object);
769 JSValue filteredValue = callReviver(object, jsString(vm, prop.string()), outValue);
770 RETURN_IF_EXCEPTION(scope, { });
771 if (filteredValue.isUndefined())
772 object->methodTable(vm)->deleteProperty(object, m_globalObject, prop);
773 else
774 object->methodTable(vm)->put(object, m_globalObject, prop, filteredValue, slot);
775 RETURN_IF_EXCEPTION(scope, { });
776 indexStack.last()++;
777 goto objectStartVisitMember;
778 }
779 stateUnknown:
780 case StateUnknown:
781 if (!inValue.isObject()) {
782 outValue = inValue;
783 break;
784 }
785 bool valueIsArray = isArray(m_globalObject, inValue);
786 RETURN_IF_EXCEPTION(scope, { });
787 if (valueIsArray)
788 goto arrayStartState;
789 goto objectStartState;
790 }
791 if (stateStack.isEmpty())
792 break;
793
794 state = stateStack.last();
795 stateStack.removeLast();
796 }
797 JSObject* finalHolder = constructEmptyObject(m_globalObject);
798 PutPropertySlot slot(finalHolder);
799 finalHolder->methodTable(vm)->put(finalHolder, m_globalObject, vm.propertyNames->emptyIdentifier, outValue, slot);
800 RETURN_IF_EXCEPTION(scope, { });
801 RELEASE_AND_RETURN(scope, callReviver(finalHolder, jsEmptyString(vm), outValue));
802}
803
804// ECMA-262 v5 15.12.2
805EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(JSGlobalObject* globalObject, CallFrame* callFrame)
806{
807 VM& vm = globalObject->vm();
808 auto scope = DECLARE_THROW_SCOPE(vm);
809 auto viewWithString = callFrame->argument(0).toString(globalObject)->viewWithUnderlyingString(globalObject);
810 RETURN_IF_EXCEPTION(scope, { });
811 StringView view = viewWithString.view;
812
813 JSValue unfiltered;
814 if (view.is8Bit()) {
815 LiteralParser<LChar> jsonParser(globalObject, view.characters8(), view.length(), StrictJSON);
816 unfiltered = jsonParser.tryLiteralParse();
817 EXCEPTION_ASSERT(!scope.exception() || !unfiltered);
818 if (!unfiltered) {
819 RETURN_IF_EXCEPTION(scope, { });
820 return throwVMError(globalObject, scope, createSyntaxError(globalObject, jsonParser.getErrorMessage()));
821 }
822 } else {
823 LiteralParser<UChar> jsonParser(globalObject, view.characters16(), view.length(), StrictJSON);
824 unfiltered = jsonParser.tryLiteralParse();
825 EXCEPTION_ASSERT(!scope.exception() || !unfiltered);
826 if (!unfiltered) {
827 RETURN_IF_EXCEPTION(scope, { });
828 return throwVMError(globalObject, scope, createSyntaxError(globalObject, jsonParser.getErrorMessage()));
829 }
830 }
831
832 if (callFrame->argumentCount() < 2)
833 return JSValue::encode(unfiltered);
834
835 JSValue function = callFrame->uncheckedArgument(1);
836 CallData callData;
837 CallType callType = getCallData(vm, function, callData);
838 if (callType == CallType::None)
839 return JSValue::encode(unfiltered);
840 scope.release();
841 Walker walker(globalObject, asObject(function), callType, callData);
842 return JSValue::encode(walker.walk(unfiltered));
843}
844
845// ECMA-262 v5 15.12.3
846EncodedJSValue JSC_HOST_CALL JSONProtoFuncStringify(JSGlobalObject* globalObject, CallFrame* callFrame)
847{
848 VM& vm = globalObject->vm();
849 auto scope = DECLARE_THROW_SCOPE(vm);
850
851 Stringifier stringifier(globalObject, callFrame->argument(1), callFrame->argument(2));
852 RETURN_IF_EXCEPTION(scope, { });
853 RELEASE_AND_RETURN(scope, JSValue::encode(stringifier.stringify(callFrame->argument(0))));
854}
855
856JSValue JSONParse(JSGlobalObject* globalObject, const String& json)
857{
858 if (json.isNull())
859 return JSValue();
860
861 if (json.is8Bit()) {
862 LiteralParser<LChar> jsonParser(globalObject, json.characters8(), json.length(), StrictJSON);
863 return jsonParser.tryLiteralParse();
864 }
865
866 LiteralParser<UChar> jsonParser(globalObject, json.characters16(), json.length(), StrictJSON);
867 return jsonParser.tryLiteralParse();
868}
869
870String JSONStringify(JSGlobalObject* globalObject, JSValue value, JSValue space)
871{
872 VM& vm = globalObject->vm();
873 auto throwScope = DECLARE_THROW_SCOPE(vm);
874 Stringifier stringifier(globalObject, jsNull(), space);
875 RETURN_IF_EXCEPTION(throwScope, { });
876 JSValue result = stringifier.stringify(value);
877 if (UNLIKELY(throwScope.exception()) || result.isUndefinedOrNull())
878 return String();
879 return result.getString(globalObject);
880}
881
882String JSONStringify(JSGlobalObject* globalObject, JSValue value, unsigned indent)
883{
884 return JSONStringify(globalObject, value, jsNumber(indent));
885}
886
887} // namespace JSC
888