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