v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
json-stringifier.cc
Go to the documentation of this file.
1// Copyright 2016 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <string_view>
8
9#include "hwy/highway.h"
10#include "src/base/strings.h"
19#include "src/objects/lookup.h"
23#include "src/objects/smi.h"
24#include "src/objects/tagged.h"
26
27namespace v8 {
28namespace internal {
29
30static constexpr char kJsonStringifierZoneName[] = "json-stringifier-zone";
31
33 public:
34 explicit JsonStringifier(Isolate* isolate);
35
41
43 Handle<JSAny> object, Handle<JSAny> replacer, Handle<Object> gap);
44
45 private:
47
48 bool InitializeReplacer(Handle<JSAny> replacer);
50
55 DirectHandle<Object> initial_holder);
56
57 // Entry point to serialize the object.
59 return Serialize_<false>(obj, false, factory()->empty_string());
60 }
61
62 // Serialize an array element.
63 // The index may serve as argument for the toJSON function.
65 int i) {
66 return Serialize_<false>(object, false,
67 Handle<Object>(Smi::FromInt(i), isolate));
68 }
69
70 // Serialize an object property.
71 // The key may or may not be serialized depending on the property.
72 // The key may also serve as argument for the toJSON function.
73 V8_INLINE Result SerializeProperty(Handle<JSAny> object, bool deferred_comma,
74 Handle<String> deferred_key) {
75 DCHECK(!deferred_key.is_null());
76 return Serialize_<true>(object, deferred_comma, deferred_key);
77 }
78
79 template <typename SrcChar, typename DestChar>
80 V8_INLINE void Append(SrcChar c) {
81 DCHECK_EQ(encoding_ == String::ONE_BYTE_ENCODING, sizeof(DestChar) == 1);
82 if (sizeof(DestChar) == 1) {
85 } else {
87 // Make sure to use unsigned extension even when SrcChar == char.
89 static_cast<std::make_unsigned_t<SrcChar>>(c);
90 }
92 }
93
94 V8_INLINE void AppendCharacter(uint8_t c) {
97 } else {
99 }
100 }
101
102 template <size_t N>
104 // Note that the literal contains the zero char.
105 constexpr size_t length = N - 1;
106 static_assert(length > 0);
107 if (length == 1) return AppendCharacter(literal[0]);
109 const uint8_t* chars = reinterpret_cast<const uint8_t*>(literal);
111 length);
115 return;
116 }
117 return AppendCString(literal);
118 }
119
120 template <typename SrcChar>
121 V8_INLINE void AppendCString(const SrcChar* s) {
123 while (*s != '\0') Append<SrcChar, uint8_t>(*s++);
124 } else {
125 while (*s != '\0') Append<SrcChar, base::uc16>(*s++);
126 }
127 }
128
129 template <typename SrcChar>
130 V8_INLINE void AppendString(std::basic_string_view<SrcChar> s) {
132 for (SrcChar c : s) Append<SrcChar, uint8_t>(c);
133 } else {
134 for (SrcChar c : s) Append<SrcChar, base::uc16>(c);
135 }
136 }
137
138 V8_INLINE bool CurrentPartCanFit(size_t length) {
140 }
141
142 // We make a rough estimate to find out if the current string can be
143 // serialized without allocating a new string part. The worst case length of
144 // an escaped character is 6. Shifting the remaining string length left by 3
145 // is a more pessimistic estimate, but faster to calculate.
147 if (length > kMaxPartLength) return false;
148 static_assert(kMaxPartLength <= (String::kMaxLength >> 3));
149 // This shift will not overflow because length is already less than the
150 // maximum part length.
151 return CurrentPartCanFit(length << 3);
152 }
153
154 void AppendStringByCopy(Tagged<String> string, size_t length,
155 const DisallowGarbageCollection& no_gc) {
156 DCHECK_EQ(length, string->length());
158 (string->IsFlat() &&
160 DCHECK(CurrentPartCanFit(length + 1));
165 string->GetCharVector<uint8_t>(no_gc).begin(), length);
166 } else {
170 string->GetCharVector<uint16_t>(no_gc).begin(), length);
171 }
172 } else {
176 string->GetCharVector<uint8_t>(no_gc).begin(), length);
177 } else {
180 string->GetCharVector<uint16_t>(no_gc).begin(), length);
181 }
182 }
185 }
186
188 {
190 Tagged<String> string = *string_handle;
191 const bool representation_ok =
193 (string->IsFlat() &&
195 if (representation_ok) {
196 size_t length = string->length();
197 while (!CurrentPartCanFit(length + 1)) {
198 Extend();
199 if (V8_UNLIKELY(overflowed_)) return;
200 }
201 AppendStringByCopy(string, length, no_gc);
202 return;
203 }
204 }
205 SerializeString<true>(string_handle);
206 }
207
208 template <typename SrcChar>
209 void AppendSubstringByCopy(const SrcChar* src, size_t count) {
212 if (sizeof(SrcChar) == 1) {
214 } else {
217 count);
218 }
219 } else {
221 count);
222 }
225 }
226
227 template <typename SrcChar>
228 V8_NOINLINE void AppendSubstring(const SrcChar* src, size_t from, size_t to) {
229 if (from == to) return;
230 DCHECK_LT(from, to);
231 size_t count = to - from;
232 while (!CurrentPartCanFit(count + 1)) {
233 Extend();
234 if (V8_UNLIKELY(overflowed_)) return;
235 }
236 AppendSubstringByCopy(src + from, count);
237 }
238
240
241 template <bool deferred_string_key>
242 Result Serialize_(Handle<JSAny> object, bool comma, Handle<Object> key);
243
244 V8_INLINE void SerializeDeferredKey(bool deferred_comma,
245 Handle<Object> deferred_key);
246
248
249 Result SerializeDouble(double number);
253
256
260
263 template <ElementsKind kind>
265 DirectHandle<JSArray> array, uint32_t length, uint32_t* slow_path_index);
266 template <ElementsKind kind>
268 DirectHandle<JSArray> array, uint32_t length, uint32_t* slow_path_index);
269 template <ElementsKind kind, typename T>
271 Tagged<JSArray> array,
272 bool can_treat_hole_as_undefined);
274 uint32_t length);
275
276 // Returns whether any escape sequences were used.
277 template <bool raw_json>
278 bool SerializeString(Handle<String> object);
279
280 template <typename DestChar>
282 public:
283 NoExtendBuilder(DestChar* start, size_t* current_index)
284 : current_index_(current_index), start_(start), cursor_(start) {}
286
287 V8_INLINE void Append(DestChar c) { *(cursor_++) = c; }
288 V8_INLINE void AppendCString(const char* s) {
289 const uint8_t* u = reinterpret_cast<const uint8_t*>(s);
290 while (*u != '\0') Append(*(u++));
291 }
292 V8_INLINE void AppendString(std::string_view str) {
295 reinterpret_cast<const uint8_t*>(str.data()), str.length()),
296 str.length());
297 }
298
299 // Appends all of the chars from the provided span, but only increases the
300 // cursor by `length`. This allows oversizing the span to the nearest
301 // convenient multiple, allowing CopyChars to run slightly faster.
303 size_t length) {
304 DCHECK_GE(chars.size(), length);
305 CopyChars(cursor_, chars.begin(), chars.size());
306 cursor_ += length;
307 }
308
309 template <typename SrcChar>
310 V8_INLINE void AppendSubstring(const SrcChar* src, size_t from, size_t to) {
311 if (from == to) return;
312 DCHECK_LT(from, to);
313 size_t count = to - from;
314 CopyChars(cursor_, src + from, count);
315 cursor_ += count;
316 }
317
318 private:
320 DestChar* start_;
321 DestChar* cursor_;
322 };
323
324 // A cache of recently seen property keys which were simple. Simple means:
325 //
326 // - Internalized, sequential, one-byte string
327 // - Contains no characters which need escaping
328 //
329 // This can be helpful because it's common for JSON to have lists of similar
330 // objects. Since property keys are internalized, we will see identical key
331 // pointers again and again, and we can use a fast path to copy those keys to
332 // the output. However, strings can be externalized any time JS runs, so the
333 // caller is responsible for checking whether a string is still the expected
334 // type. This cache is cleared on GC, since the GC could move those strings.
335 // Using Handles for the cache has been tried, but is too expensive to set up
336 // when JSON.stringify is called for tiny inputs.
338 public:
339 explicit SimplePropertyKeyCache(Isolate* isolate) : isolate_(isolate) {
340 Clear();
341 isolate->main_thread_local_heap()->AddGCEpilogueCallback(
343 }
344
349
351 ReadOnlyRoots roots(isolate_);
352 if (string->map() == roots.internalized_one_byte_string_map()) {
353 keys_[GetIndex(string)] = MaybeCompress(string);
354 }
355 }
356
358 return keys_[GetIndex(string)] == MaybeCompress(string);
359 }
360
361 private:
362 size_t GetIndex(Tagged<String> string) {
363 // Short strings are 16 bytes long in pointer-compression builds, so the
364 // lower four bits of the pointer may not provide much entropy.
365 return (string.ptr() >> 4) & kIndexMask;
366 }
367
371 : static_cast<Tagged_t>(string.ptr());
372 }
373
375
376 static void UpdatePointersCallback(void* cache) {
377 reinterpret_cast<SimplePropertyKeyCache*>(cache)->Clear();
378 }
379
380 static constexpr size_t kSizeBits = 6;
381 static constexpr size_t kSize = 1 << kSizeBits;
382 static constexpr size_t kIndexMask = kSize - 1;
383
386 };
387
388 // Returns whether any escape sequences were used.
389 template <typename SrcChar, typename DestChar, bool raw_json>
392
393 // Returns whether any escape sequences were used.
394 template <typename SrcChar, typename DestChar, bool raw_json>
396 const DisallowGarbageCollection& no_gc);
397
398 // Tries to do fast-path serialization for a property key, and returns whether
399 // it was successful.
400 template <typename DestChar>
402 const DisallowGarbageCollection& no_gc);
403
404 V8_INLINE void NewLine();
408 V8_INLINE void Separator(bool first);
409
411 DirectHandle<Object> inital_holder);
412
414 void StackPop();
415
416 // Uses the current stack_ to provide a detailed error message of
417 // the objects involved in the circular structure.
419 DirectHandle<Object> last_key, size_t start_index);
420 // The prefix and postfix count do NOT include the starting and
421 // closing lines of the error message.
422 static constexpr int kCircularErrorMessagePrefixCount = 2;
423 static constexpr int kCircularErrorMessagePostfixCount = 1;
424
425 static constexpr size_t kInitialPartLength = 2048;
426 static constexpr size_t kMaxPartLength = 16 * 1024;
427 static constexpr size_t kPartLengthGrowthFactor = 2;
428
429 Factory* factory() { return isolate_->factory(); }
430
431 V8_NOINLINE void Extend();
433
448
449 using KeyObject = std::pair<Handle<Object>, Handle<Object>>;
450 std::vector<KeyObject> stack_;
451
454};
455
456namespace {
457
458constexpr int kJsonEscapeTableEntrySize = 8;
459
460// Translation table to escape Latin1 characters.
461// Table entries start at a multiple of 8 and are null-terminated.
462constexpr const char* const JsonEscapeTable =
463 "\\u0000\0 \\u0001\0 \\u0002\0 \\u0003\0 "
464 "\\u0004\0 \\u0005\0 \\u0006\0 \\u0007\0 "
465 "\\b\0 \\t\0 \\n\0 \\u000b\0 "
466 "\\f\0 \\r\0 \\u000e\0 \\u000f\0 "
467 "\\u0010\0 \\u0011\0 \\u0012\0 \\u0013\0 "
468 "\\u0014\0 \\u0015\0 \\u0016\0 \\u0017\0 "
469 "\\u0018\0 \\u0019\0 \\u001a\0 \\u001b\0 "
470 "\\u001c\0 \\u001d\0 \\u001e\0 \\u001f\0 "
471 " \0 !\0 \\\"\0 #\0 "
472 "$\0 %\0 &\0 '\0 "
473 "(\0 )\0 *\0 +\0 "
474 ",\0 -\0 .\0 /\0 "
475 "0\0 1\0 2\0 3\0 "
476 "4\0 5\0 6\0 7\0 "
477 "8\0 9\0 :\0 ;\0 "
478 "<\0 =\0 >\0 ?\0 "
479 "@\0 A\0 B\0 C\0 "
480 "D\0 E\0 F\0 G\0 "
481 "H\0 I\0 J\0 K\0 "
482 "L\0 M\0 N\0 O\0 "
483 "P\0 Q\0 R\0 S\0 "
484 "T\0 U\0 V\0 W\0 "
485 "X\0 Y\0 Z\0 [\0 "
486 "\\\\\0 ]\0 ^\0 _\0 ";
487
488constexpr bool JsonDoNotEscapeFlagTable[] = {
489 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
490 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
491 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
492 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
493 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
494 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
495 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
496 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
497 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
498 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
499 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
500};
501
502template <typename Char>
503constexpr bool DoNotEscape(Char c);
504
505template <>
506constexpr bool DoNotEscape(uint8_t c) {
507 // https://tc39.github.io/ecma262/#table-json-single-character-escapes
508 return JsonDoNotEscapeFlagTable[c];
509}
510
511template <>
512constexpr bool DoNotEscape(uint16_t c) {
513 // https://tc39.github.io/ecma262/#table-json-single-character-escapes
514 return (c >= 0x20 && c <= 0x21) ||
515 (c >= 0x23 && c != 0x5C && (c < 0xD800 || c > 0xDFFF));
516}
517
518// Checks if characters need escaping in a packed input (4 bytes in uint32_t).
519constexpr bool NeedsEscape(uint32_t input) {
520 constexpr uint32_t mask_0x20 = 0x20202020u;
521 constexpr uint32_t mask_0x22 = 0x22222222u;
522 constexpr uint32_t mask_0x5c = 0x5C5C5C5Cu;
523 constexpr uint32_t mask_0x01 = 0x01010101u;
524 constexpr uint32_t mask_msb = 0x80808080u;
525 // Escape control characters (< 0x20).
526 const uint32_t has_lt_0x20 = input - mask_0x20;
527 // Escape double quotation mark (0x22).
528 const uint32_t has_0x22 = (input ^ mask_0x22) - mask_0x01;
529 // Escape backslash (0x5C).
530 const uint32_t has_0x5c = (input ^ mask_0x5c) - mask_0x01;
531 // Chars >= 0x7F don't need escaping.
532 const uint32_t result_mask = ~input & mask_msb;
533 const uint32_t result = ((has_lt_0x20 | has_0x22 | has_0x5c) & result_mask);
534 return result != 0;
535}
536
537bool CanFastSerializeJSArray(Isolate* isolate, Tagged<JSArray> object) {
538 // If the no elements protector is intact, Array.prototype and
539 // Object.prototype are guaranteed to not have elements in any native context.
540 if (!Protectors::IsNoElementsIntact(isolate)) return false;
541 Tagged<Map> map = object->map(isolate);
542 Tagged<NativeContext> native_context = map->map(isolate)->native_context();
543 Tagged<HeapObject> proto = map->prototype();
544 return native_context->get(Context::INITIAL_ARRAY_PROTOTYPE_INDEX) == proto;
545}
546
547V8_INLINE bool CanFastSerializeJSObject(Tagged<JSObject> raw_object,
548 Isolate* isolate) {
550 if (IsCustomElementsReceiverMap(raw_object->map())) return false;
551 if (!raw_object->HasFastProperties()) return false;
552 auto roots = ReadOnlyRoots(isolate);
553 auto elements = raw_object->elements();
554 return elements == roots.empty_fixed_array() ||
555 elements == roots.empty_slow_element_dictionary();
556}
557
558} // namespace
559
561 : isolate_(isolate),
562 encoding_(String::ONE_BYTE_ENCODING),
563 gap_(nullptr),
564 two_byte_ptr_(nullptr),
565 indent_(0),
566 part_length_(kInitialPartLength),
567 current_index_(0),
568 stack_nesting_level_(0),
569 overflowed_(false),
570 need_stack_(false),
571 stack_(),
572 key_cache_(isolate) {
575}
576
578 Handle<JSAny> replacer,
579 Handle<Object> gap) {
580 if (!InitializeReplacer(replacer)) {
583 }
584 if (!IsUndefined(*gap, isolate_) && !InitializeGap(gap)) {
587 }
588 Result result = SerializeObject(object);
589 if (result == NEED_STACK) {
590 indent_ = 0;
591 current_index_ = 0;
592 result = SerializeObject(object);
593 }
594 if (result == UNCHANGED) return factory()->undefined_value();
595 if (result == SUCCESS) {
597 THROW_NEW_ERROR(isolate_, NewInvalidStringLengthError());
598 }
600 return isolate_->factory()
602 reinterpret_cast<char*>(one_byte_ptr_), current_index_))
603 .ToHandleChecked();
604 } else {
607 }
608 }
612}
613
615 DCHECK(property_list_.is_null());
616 DCHECK(replacer_function_.is_null());
617 Maybe<bool> is_array = Object::IsArray(replacer);
618 if (is_array.IsNothing()) return false;
619 if (is_array.FromJust()) {
620 HandleScope handle_scope(isolate_);
622 DirectHandle<Object> length_obj;
624 isolate_, length_obj,
626 false);
627 uint32_t length;
628 if (!Object::ToUint32(*length_obj, &length)) length = kMaxUInt32;
629 for (uint32_t i = 0; i < length; i++) {
630 Handle<Object> element;
633 isolate_, element, Object::GetElement(isolate_, replacer, i), false);
634 if (IsNumber(*element) || IsString(*element)) {
636 isolate_, key, Object::ToString(isolate_, element), false);
637 } else if (IsJSPrimitiveWrapper(*element)) {
639 isolate_);
640 if (IsNumber(*value) || IsString(*value)) {
642 isolate_, key, Object::ToString(isolate_, element), false);
643 }
644 }
645 if (key.is_null()) continue;
646 // Object keys are internalized, so do it here.
648 MaybeHandle<OrderedHashSet> set_candidate =
650 if (!set_candidate.ToHandle(&set)) {
652 return false;
653 }
654 }
658 } else if (IsCallable(*replacer)) {
660 }
661 return true;
662}
663
666 HandleScope scope(isolate_);
667 if (IsJSPrimitiveWrapper(*gap)) {
669 isolate_);
670 if (IsString(*value)) {
672 Object::ToString(isolate_, gap), false);
673 } else if (IsNumber(*value)) {
675 Object::ToNumber(isolate_, gap), false);
676 }
677 }
678
679 if (IsString(*gap)) {
680 auto gap_string = Cast<String>(gap);
681 if (gap_string->length() > 0) {
682 uint32_t gap_length = std::min(gap_string->length(), 10u);
683 gap_ = NewArray<base::uc16>(gap_length + 1);
684 String::WriteToFlat(*gap_string, gap_, 0, gap_length);
685 for (uint32_t i = 0; i < gap_length; i++) {
688 break;
689 }
690 }
691 gap_[gap_length] = '\0';
692 }
693 } else if (IsNumber(*gap)) {
694 double value = std::min(Object::NumberValue(*gap), 10.0);
695 if (value > 0) {
696 uint32_t gap_length = DoubleToUint32(value);
697 gap_ = NewArray<base::uc16>(gap_length + 1);
698 for (uint32_t i = 0; i < gap_length; i++) gap_[i] = ' ';
699 gap_[gap_length] = '\0';
700 }
701 }
702 return true;
703}
704
707 HandleScope scope(isolate_);
708
709 // Retrieve toJSON function. The LookupIterator automatically handles
710 // the ToObject() equivalent ("GetRoot") if {object} is a BigInt.
712 LookupIterator it(isolate_, object, factory()->toJSON_string(),
715 if (!IsCallable(*fun)) return object;
716
717 // Call toJSON function.
718 if (IsSmi(*key)) key = factory()->NumberToString(key);
722 isolate_, fun, object, base::VectorOf(args))));
723 return scope.CloseAndEscape(object);
724}
725
739
741 DirectHandle<Object> value, DirectHandle<Object> initial_holder) {
742 if (stack_.empty()) {
744 factory()->NewJSObject(isolate_->object_function());
745 JSObject::AddProperty(isolate_, holder, factory()->empty_string(),
746 initial_holder, NONE);
747 return holder;
748 } else {
749 return DirectHandle<JSReceiver>(Cast<JSReceiver>(*stack_.back().second),
750 isolate_);
751 }
752}
753
756 if (!need_stack_) {
759 need_stack_ = true;
760 return NEED_STACK;
761 }
762 return SUCCESS;
763 }
765 if (check.HasOverflowed()) {
767 return EXCEPTION;
768 }
769
770 {
772 Tagged<Object> raw_obj = *object;
773 size_t size = stack_.size();
774 for (size_t i = 0; i < size; ++i) {
775 if (*stack_[i].second == raw_obj) {
776 AllowGarbageCollection allow_to_return_error;
777 Handle<String> circle_description =
779 DirectHandle<Object> error = factory()->NewTypeError(
780 MessageTemplate::kCircularStructure, circle_description);
781 isolate_->Throw(*error);
782 return EXCEPTION;
783 }
784 }
785 }
786 stack_.emplace_back(key, object);
787 return SUCCESS;
788}
789
791 if V8_LIKELY (!need_stack_) {
793 return;
794 }
795 stack_.pop_back();
796}
797
799 public:
801 : builder_(isolate) {}
802
805 builder_.AppendCStringLiteral("starting at object with constructor ");
806 AppendConstructorName(start_object);
807 }
808
815
818 AppendKey(closing_key);
819 builder_.AppendCStringLiteral(" closes the circle");
820 }
821
826
828
829 private:
837
838 // A key can either be a string, the empty string or a Smi.
840 if (IsSmi(*key)) {
843 return;
844 }
845
846 CHECK(IsString(*key));
847 DirectHandle<String> key_as_string = Cast<String>(key);
848 if (key_as_string->length() == 0) {
849 builder_.AppendCStringLiteral("<anonymous>");
850 } else {
851 builder_.AppendCStringLiteral("property '");
852 builder_.AppendString(key_as_string);
854 }
855 }
856
858 static_assert(Smi::kMaxValue <= 2147483647);
859 static_assert(Smi::kMinValue >= -2147483648);
860 // sizeof(string) includes \0.
861 static constexpr uint32_t kBufferSize = sizeof("-2147483648") - 1;
862 char chars[kBufferSize];
863 base::Vector<char> buffer(chars, kBufferSize);
864 builder_.AppendString(IntToStringView(smi.value(), buffer));
865 }
866
868 static constexpr const char* kStartPrefix = "\n --> ";
869 static constexpr const char* kEndPrefix = "\n --- ";
870 static constexpr const char* kLinePrefix = "\n | ";
871};
872
874 DirectHandle<Object> last_key, size_t start_index) {
875 DCHECK(start_index < stack_.size());
877
878 // We track the index to be printed next for better readability.
879 size_t index = start_index;
880 const size_t stack_size = stack_.size();
881
882 builder.AppendStartLine(stack_[index++].second);
883
884 // Append a maximum of kCircularErrorMessagePrefixCount normal lines.
885 const size_t prefix_end =
886 std::min(stack_size, index + kCircularErrorMessagePrefixCount);
887 for (; index < prefix_end; ++index) {
888 builder.AppendNormalLine(stack_[index].first, stack_[index].second);
889 }
890
891 // If the circle consists of too many objects, we skip them and just
892 // print an ellipsis.
893 if (stack_size > index + kCircularErrorMessagePostfixCount) {
894 builder.AppendEllipsis();
895 }
896
897 // Since we calculate the postfix lines from the back of the stack,
898 // we have to ensure that lines are not printed twice.
899 index = std::max(index, stack_size - kCircularErrorMessagePostfixCount);
900 for (; index < stack_size; ++index) {
901 builder.AppendNormalLine(stack_[index].first, stack_[index].second);
902 }
903
904 builder.AppendClosingLine(last_key);
905
908 indirect_handle(builder.Finish(), isolate_),
909 factory()->empty_string());
910 return result;
911}
912
914 for (PrototypeIterator iter(isolate, object, kStartAtReceiver);
915 !iter.IsAtEnd(); iter.Advance()) {
916 if (iter.GetCurrent()->map()->may_have_interesting_properties()) {
917 return true;
918 }
919 }
920 return false;
921}
922
923template <bool deferred_string_key>
925 bool comma,
927 StackLimitCheck interrupt_check(isolate_);
928 if (interrupt_check.InterruptRequested() &&
929 IsException(isolate_->stack_guard()->HandleInterrupts(), isolate_)) {
930 return EXCEPTION;
931 }
932
933 DirectHandle<JSAny> initial_value = object;
934 PtrComprCageBase cage_base(isolate_);
935 if (!IsSmi(*object)) {
936 InstanceType instance_type =
937 Cast<HeapObject>(*object)->map(cage_base)->instance_type();
938 if ((InstanceTypeChecker::IsJSReceiver(instance_type) &&
940 InstanceTypeChecker::IsBigInt(instance_type)) {
941 if (!need_stack_ && stack_nesting_level_ > 0) {
942 need_stack_ = true;
943 return NEED_STACK;
944 }
945 need_stack_ = true;
947 isolate_, object, ApplyToJsonFunction(object, key), EXCEPTION);
948 }
949 }
950 if (!replacer_function_.is_null()) {
951 need_stack_ = true;
953 isolate_, object, ApplyReplacerFunction(object, key, initial_value),
954 EXCEPTION);
955 }
956
957 if (IsSmi(*object)) {
958 if (deferred_string_key) SerializeDeferredKey(comma, key);
959 return SerializeSmi(Cast<Smi>(*object));
960 }
961
962 InstanceType instance_type =
963 Cast<HeapObject>(*object)->map(cage_base)->instance_type();
964 switch (instance_type) {
965 case HEAP_NUMBER_TYPE:
966 if (deferred_string_key) SerializeDeferredKey(comma, key);
968 case BIGINT_TYPE:
970 *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON));
971 return EXCEPTION;
972 case ODDBALL_TYPE:
973 switch (Cast<Oddball>(*object)->kind()) {
974 case Oddball::kFalse:
975 if (deferred_string_key) SerializeDeferredKey(comma, key);
976 AppendCStringLiteral("false");
977 return SUCCESS;
978 case Oddball::kTrue:
979 if (deferred_string_key) SerializeDeferredKey(comma, key);
980 AppendCStringLiteral("true");
981 return SUCCESS;
982 case Oddball::kNull:
983 if (deferred_string_key) SerializeDeferredKey(comma, key);
984 AppendCStringLiteral("null");
985 return SUCCESS;
986 default:
987 return UNCHANGED;
988 }
989 case JS_ARRAY_TYPE:
990 if (deferred_string_key) SerializeDeferredKey(comma, key);
991 return SerializeJSArray(Cast<JSArray>(object), key);
992 case JS_PRIMITIVE_WRAPPER_TYPE:
993 if (!need_stack_) {
994 need_stack_ = true;
995 return NEED_STACK;
996 }
997 if (deferred_string_key) SerializeDeferredKey(comma, key);
999 case SYMBOL_TYPE:
1000 return UNCHANGED;
1001 case JS_RAW_JSON_TYPE:
1002 if (deferred_string_key) SerializeDeferredKey(comma, key);
1003 {
1004 DirectHandle<JSRawJson> raw_json_obj = Cast<JSRawJson>(object);
1005 Handle<String> raw_json;
1006 if (raw_json_obj->HasInitialLayout(isolate_)) {
1007 // Fast path: the object returned by JSON.rawJSON has its initial map
1008 // intact.
1009 raw_json = Cast<String>(handle(
1010 raw_json_obj->InObjectPropertyAt(JSRawJson::kRawJsonInitialIndex),
1011 isolate_));
1012 } else {
1013 // Slow path: perform a property get for "rawJSON". Because raw JSON
1014 // objects are created frozen, it is still guaranteed that there will
1015 // be a property named "rawJSON" that is a String. Their initial maps
1016 // only change due to VM-internal operations like being optimized for
1017 // being used as a prototype.
1018 raw_json = Cast<String>(
1019 JSObject::GetProperty(isolate_, raw_json_obj,
1020 isolate_->factory()->raw_json_string())
1021 .ToHandleChecked());
1022 }
1023 AppendString(raw_json);
1024 }
1025 return SUCCESS;
1026 case HOLE_TYPE:
1027 UNREACHABLE();
1028#if V8_ENABLE_WEBASSEMBLY
1029 case WASM_STRUCT_TYPE:
1030 case WASM_ARRAY_TYPE:
1031 return UNCHANGED;
1032#endif
1033 default:
1034 if (InstanceTypeChecker::IsString(instance_type)) {
1035 if (deferred_string_key) SerializeDeferredKey(comma, key);
1037 return SUCCESS;
1038 } else {
1039 // Make sure that we have a JSReceiver before we cast it to one.
1040 // If we ever leak an internal object that is not a JSReceiver it could
1041 // end up here and lead to a type confusion.
1042 CHECK(IsJSReceiver(*object));
1043 if (IsCallable(Cast<HeapObject>(*object), cage_base)) return UNCHANGED;
1044 // Go to slow path for global proxy and objects requiring access checks.
1045 if (deferred_string_key) SerializeDeferredKey(comma, key);
1046 if (InstanceTypeChecker::IsJSProxy(instance_type)) {
1047 return SerializeJSProxy(Cast<JSProxy>(object), key);
1048 }
1049 // WASM_{STRUCT,ARRAY}_TYPE are handled in `case:` blocks above.
1050 DCHECK(IsJSObject(*object));
1051 return SerializeJSObject(Cast<JSObject>(object), key);
1052 }
1053 }
1054
1055 UNREACHABLE();
1056}
1057
1060 Tagged<Object> raw = object->value();
1061 if (IsString(raw)) {
1064 isolate_, value, Object::ToString(isolate_, object), EXCEPTION);
1066 } else if (IsNumber(raw)) {
1069 isolate_, value, Object::ToNumber(isolate_, object), EXCEPTION);
1070 if (IsSmi(*value)) return SerializeSmi(Cast<Smi>(*value));
1072 } else if (IsBigInt(raw)) {
1073 isolate_->Throw(
1074 *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON));
1075 return EXCEPTION;
1076 } else if (IsBoolean(raw)) {
1077 if (IsTrue(raw, isolate_)) {
1078 AppendCStringLiteral("true");
1079 } else {
1080 AppendCStringLiteral("false");
1081 }
1082 } else {
1083 // ES6 24.3.2.1 step 10.c, serialize as an ordinary JSObject.
1084 return SerializeJSObject(object, key);
1085 }
1086 return SUCCESS;
1087}
1088
1090 static_assert(Smi::kMaxValue <= 2147483647);
1091 static_assert(Smi::kMinValue >= -2147483648);
1092 // sizeof(string) includes \0.
1093 static constexpr uint32_t kBufferSize = sizeof("-2147483648") - 1;
1094 char chars[kBufferSize];
1095 base::Vector<char> buffer(chars, kBufferSize);
1096 AppendString(IntToStringView(object.value(), buffer));
1097 return SUCCESS;
1098}
1099
1101 if (std::isinf(number) || std::isnan(number)) {
1102 AppendCStringLiteral("null");
1103 return SUCCESS;
1104 }
1105 static constexpr uint32_t kBufferSize = 100;
1106 char chars[kBufferSize];
1107 base::Vector<char> buffer(chars, kBufferSize);
1108 std::string_view str = DoubleToStringView(number, buffer);
1109 AppendString(str);
1110 return SUCCESS;
1111}
1112
1115 uint32_t length = 0;
1116 CHECK(Object::ToArrayLength(object->length(), &length));
1117 DCHECK(!IsAccessCheckNeeded(*object));
1118 if (length == 0) {
1120 return SUCCESS;
1121 }
1122
1123 Result stack_push = StackPush(object, key);
1124 if (stack_push != SUCCESS) return stack_push;
1125
1126 AppendCharacter('[');
1127 Indent();
1128 uint32_t slow_path_index = 0;
1130 if (replacer_function_.is_null()) {
1131#define CASE_WITH_INTERRUPT(kind) \
1132 case kind: \
1133 result = SerializeFixedArrayWithInterruptCheck<kind>(object, length, \
1134 &slow_path_index); \
1135 break;
1136#define CASE_WITH_TRANSITION(kind) \
1137 case kind: \
1138 result = SerializeFixedArrayWithPossibleTransitions<kind>( \
1139 object, length, &slow_path_index); \
1140 break;
1141
1142 switch (object->GetElementsKind()) {
1149 default:
1150 break;
1151 }
1152
1153#undef CASE_WITH_TRANSITION
1154#undef CASE_WITH_INTERRUPT
1155 }
1156 if (result == UNCHANGED) {
1157 // Slow path for non-fast elements and fall-back in edge cases.
1158 result = SerializeArrayLikeSlow(object, slow_path_index, length);
1159 }
1160 if (result != SUCCESS) return result;
1161 Unindent();
1162 NewLine();
1163 AppendCharacter(']');
1164 StackPop();
1165 return SUCCESS;
1166}
1167
1168template <ElementsKind kind>
1170 DirectHandle<JSArray> array, uint32_t length, uint32_t* slow_path_index) {
1171 static_assert(IsSmiElementsKind(kind) || IsDoubleElementsKind(kind));
1172 using ArrayT = std::conditional_t<IsDoubleElementsKind(kind),
1174
1175 StackLimitCheck interrupt_check(isolate_);
1176 constexpr uint32_t kInterruptLength = 4000;
1177 uint32_t limit = std::min(length, kInterruptLength);
1178 constexpr uint32_t kMaxAllowedFastPackedLength =
1179 std::numeric_limits<uint32_t>::max() - kInterruptLength;
1180 static_assert(FixedArray::kMaxLength < kMaxAllowedFastPackedLength);
1181
1182 constexpr bool is_holey = IsHoleyElementsKind(kind);
1183 bool bailout_on_hole =
1184 is_holey ? !CanFastSerializeJSArray(isolate_, *array) : true;
1185
1186 uint32_t i = 0;
1187 while (true) {
1188 for (; i < limit; i++) {
1190 Cast<ArrayT>(array->elements()), i, *array, bailout_on_hole);
1191 if constexpr (is_holey) {
1192 if (result != SUCCESS) {
1193 *slow_path_index = i;
1194 return result;
1195 }
1196 } else {
1197 USE(result);
1199 }
1200 }
1201 if (i >= length) return SUCCESS;
1202 DCHECK_LT(limit, kMaxAllowedFastPackedLength);
1203 limit = std::min(length, limit + kInterruptLength);
1204 if (interrupt_check.InterruptRequested() &&
1205 IsException(isolate_->stack_guard()->HandleInterrupts(), isolate_)) {
1206 return EXCEPTION;
1207 }
1208 }
1209 return SUCCESS;
1210}
1211
1212template <ElementsKind kind>
1215 DirectHandle<JSArray> array, uint32_t length, uint32_t* slow_path_index) {
1216 static_assert(IsObjectElementsKind(kind));
1217
1218 HandleScope handle_scope(isolate_);
1219 DirectHandle<Object> old_length(array->length(), isolate_);
1220 constexpr bool is_holey = IsHoleyElementsKind(kind);
1221 bool should_check_treat_hole_as_undefined = true;
1222 for (uint32_t i = 0; i < length; i++) {
1223 if (array->length() != *old_length || kind != array->GetElementsKind()) {
1224 // Array was modified during SerializeElement.
1225 *slow_path_index = i;
1226 return UNCHANGED;
1227 }
1228 Tagged<Object> current_element =
1229 Cast<FixedArray>(array->elements())->get(i);
1230 if (is_holey && IsTheHole(current_element)) {
1231 if (should_check_treat_hole_as_undefined) {
1232 if (!CanFastSerializeJSArray(isolate_, *array)) {
1233 *slow_path_index = i;
1234 return UNCHANGED;
1235 }
1236 should_check_treat_hole_as_undefined = false;
1237 }
1238 Separator(i == 0);
1239 AppendCStringLiteral("null");
1240 } else {
1241 Separator(i == 0);
1243 isolate_, handle(Cast<JSAny>(current_element), isolate_), i);
1244 if (result == UNCHANGED) {
1245 AppendCStringLiteral("null");
1246 } else if (result != SUCCESS) {
1247 return result;
1248 }
1249 if constexpr (is_holey) {
1250 should_check_treat_hole_as_undefined = true;
1251 }
1252 }
1253 }
1254 return SUCCESS;
1255}
1256
1257template <ElementsKind kind, typename T>
1259 Tagged<T> elements, uint32_t i, Tagged<JSArray> array,
1260 bool bailout_on_hole) {
1261 if constexpr (IsHoleyElementsKind(kind)) {
1262 if (elements->is_the_hole(isolate_, i)) {
1263 if (bailout_on_hole) return UNCHANGED;
1264 Separator(i == 0);
1265 AppendCStringLiteral("null");
1266 return SUCCESS;
1267 }
1268 }
1269 DCHECK(!elements->is_the_hole(isolate_, i));
1270 Separator(i == 0);
1271 if constexpr (IsSmiElementsKind(kind)) {
1272 SerializeSmi(Cast<Smi>(elements->get(i)));
1273 } else if constexpr (IsDoubleElementsKind(kind)) {
1274 SerializeDouble(elements->get_scalar(i));
1275 } else {
1276 UNREACHABLE();
1277 }
1278 return SUCCESS;
1279}
1280
1282 DirectHandle<JSReceiver> object, uint32_t start, uint32_t length) {
1283 if (!need_stack_) {
1284 need_stack_ = true;
1285 return NEED_STACK;
1286 }
1287 // We need to write out at least two characters per array element.
1288 static constexpr uint32_t kMaxSerializableArrayLength =
1290 if (length > kMaxSerializableArrayLength) {
1292 return EXCEPTION;
1293 }
1294 HandleScope handle_scope(isolate_);
1295 for (uint32_t i = start; i < length; i++) {
1296 Separator(i == 0);
1297 Handle<Object> element;
1299 isolate_, element, JSReceiver::GetElement(isolate_, object, i),
1300 EXCEPTION);
1302 if (result == SUCCESS) continue;
1303 if (result == UNCHANGED) {
1304 // Detect overflow sooner for large sparse arrays.
1305 if (overflowed_) {
1307 return EXCEPTION;
1308 }
1309 AppendCStringLiteral("null");
1310 } else {
1311 return result;
1312 }
1313 }
1314 return SUCCESS;
1315}
1316
1319 PtrComprCageBase cage_base(isolate_);
1320 HandleScope handle_scope(isolate_);
1321
1322 if (!property_list_.is_null() ||
1323 !CanFastSerializeJSObject(*object, isolate_)) {
1324 if (!need_stack_) {
1325 need_stack_ = true;
1326 return NEED_STACK;
1327 }
1328 Result stack_push = StackPush(object, key);
1329 if (stack_push != SUCCESS) return stack_push;
1331 if (result != SUCCESS) return result;
1332 StackPop();
1333 return SUCCESS;
1334 }
1335
1336 DCHECK(!IsJSGlobalProxy(*object));
1337 DCHECK(!object->HasIndexedInterceptor());
1338 DCHECK(!object->HasNamedInterceptor());
1339
1340 DirectHandle<Map> map(object->map(cage_base), isolate_);
1341 if (map->NumberOfOwnDescriptors() == 0) {
1343 return SUCCESS;
1344 }
1345
1346 Result stack_push = StackPush(object, key);
1347 if (stack_push != SUCCESS) return stack_push;
1348 AppendCharacter('{');
1349 Indent();
1350 bool comma = false;
1351 for (InternalIndex i : map->IterateOwnDescriptors()) {
1352 Handle<String> key_name;
1354 {
1356 Tagged<DescriptorArray> descriptors =
1357 map->instance_descriptors(cage_base);
1358 Tagged<Name> name = descriptors->GetKey(i);
1359 // TODO(rossberg): Should this throw?
1360 if (!IsString(name, cage_base)) continue;
1361 key_name = handle(Cast<String>(name), isolate_);
1362 details = descriptors->GetDetails(i);
1363 }
1364 if (details.IsDontEnum()) continue;
1366 if (details.location() == PropertyLocation::kField &&
1367 *map == object->map(cage_base)) {
1369 FieldIndex field_index = FieldIndex::ForDetails(*map, details);
1370 if (replacer_function_.is_null()) {
1371 // If there's no replacer function, read the raw property to avoid
1372 // reboxing doubles in mutable boxes.
1373 property = handle(object->RawFastPropertyAt(field_index), isolate_);
1374 } else {
1375 // Rebox the value if there is a replacer function since it could change
1376 // the value in the box.
1377 property = JSObject::FastPropertyAt(
1378 isolate_, object, details.representation(), field_index);
1379 }
1380 } else {
1381 if (!need_stack_) {
1382 need_stack_ = true;
1383 return NEED_STACK;
1384 }
1386 isolate_, property,
1388 EXCEPTION);
1389 }
1390 Result result = SerializeProperty(property, comma, key_name);
1391 if (!comma && result == SUCCESS) comma = true;
1392 if (result == EXCEPTION || result == NEED_STACK) return result;
1393 }
1394 Unindent();
1395 if (comma) NewLine();
1396 AppendCharacter('}');
1397 StackPop();
1398 return SUCCESS;
1399}
1400
1402 DirectHandle<JSReceiver> object) {
1404 if (contents.is_null()) {
1410 EXCEPTION);
1411 }
1412 AppendCharacter('{');
1413 Indent();
1414 bool comma = false;
1415 for (int i = 0; i < contents->length(); i++) {
1420 EXCEPTION);
1421 Result result = SerializeProperty(Cast<JSAny>(property), comma, key);
1422 if (!comma && result == SUCCESS) comma = true;
1423 if (result == EXCEPTION || result == NEED_STACK) return result;
1424 }
1425 Unindent();
1426 if (comma) NewLine();
1427 AppendCharacter('}');
1428 return SUCCESS;
1429}
1430
1433 HandleScope scope(isolate_);
1434 Result stack_push = StackPush(object, key);
1435 if (stack_push != SUCCESS) return stack_push;
1436 Maybe<bool> is_array = Object::IsArray(object);
1437 if (is_array.IsNothing()) return EXCEPTION;
1438 if (is_array.FromJust()) {
1439 DirectHandle<Object> length_object;
1441 isolate_, length_object,
1443 EXCEPTION);
1444 uint32_t length;
1445 if (!Object::ToUint32(*length_object, &length)) {
1446 // Technically, we need to be able to handle lengths outside the
1447 // uint32_t range. However, we would run into string size overflow
1448 // if we tried to stringify such an array.
1450 return EXCEPTION;
1451 }
1452 AppendCharacter('[');
1453 Indent();
1454 Result result = SerializeArrayLikeSlow(object, 0, length);
1455 if (result != SUCCESS) return result;
1456 Unindent();
1457 if (length > 0) NewLine();
1458 AppendCharacter(']');
1459 } else {
1461 if (result != SUCCESS) return result;
1462 }
1463 StackPop();
1464 return SUCCESS;
1465}
1466
1467template <typename SrcChar, typename DestChar, bool raw_json>
1470 // Assert that base::uc16 character is not truncated down to 8 bit.
1471 // The <base::uc16, char> version of this method must not be called.
1472 DCHECK(sizeof(DestChar) >= sizeof(SrcChar));
1473 bool required_escaping = false;
1474 size_t uncopied_src_index = 0; // Index of first char not copied yet.
1475 for (size_t i = 0; i < src.size(); i++) {
1476 SrcChar c = src[i];
1477 if (raw_json || DoNotEscape(c)) {
1478 continue;
1479 } else if (sizeof(SrcChar) != 1 &&
1480 base::IsInRange(c, static_cast<SrcChar>(0xD800),
1481 static_cast<SrcChar>(0xDFFF))) {
1482 // The current character is a surrogate.
1483 required_escaping = true;
1484 dest->AppendSubstring(src.data(), uncopied_src_index, i);
1485
1486 char double_to_radix_chars[kDoubleToRadixMaxChars];
1487 base::Vector<char> double_to_radix_buffer =
1488 base::ArrayVector(double_to_radix_chars);
1489 if (c <= 0xDBFF) {
1490 // The current character is a leading surrogate.
1491 if (i + 1 < src.size()) {
1492 // There is a next character.
1493 SrcChar next = src[i + 1];
1494 if (base::IsInRange(next, static_cast<SrcChar>(0xDC00),
1495 static_cast<SrcChar>(0xDFFF))) {
1496 // The next character is a trailing surrogate, meaning this is a
1497 // surrogate pair.
1498 dest->Append(c);
1499 dest->Append(next);
1500 i++;
1501 } else {
1502 // The next character is not a trailing surrogate. Thus, the
1503 // current character is a lone leading surrogate.
1504 dest->AppendCString("\\u");
1505 std::string_view hex =
1506 DoubleToRadixStringView(c, 16, double_to_radix_buffer);
1507 dest->AppendString(hex);
1508 }
1509 } else {
1510 // There is no next character. Thus, the current character is a lone
1511 // leading surrogate.
1512 dest->AppendCString("\\u");
1513 std::string_view hex =
1514 DoubleToRadixStringView(c, 16, double_to_radix_buffer);
1515 dest->AppendString(hex);
1516 }
1517 } else {
1518 // The current character is a lone trailing surrogate. (If it had been
1519 // preceded by a leading surrogate, we would've ended up in the other
1520 // branch earlier on, and the current character would've been handled
1521 // as part of the surrogate pair already.)
1522 dest->AppendCString("\\u");
1523 std::string_view hex =
1524 DoubleToRadixStringView(c, 16, double_to_radix_buffer);
1525 dest->AppendString(hex);
1526 }
1527 uncopied_src_index = i + 1;
1528 } else {
1529 required_escaping = true;
1530 dest->AppendSubstring(src.data(), uncopied_src_index, i);
1531 DCHECK_LT(c, 0x60);
1532 dest->AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
1533 uncopied_src_index = i + 1;
1534 }
1535 }
1536 dest->AppendSubstring(src.data(), uncopied_src_index, src.size());
1537 return required_escaping;
1538}
1539
1540template <typename SrcChar, typename DestChar, bool raw_json>
1542 const DisallowGarbageCollection& no_gc) {
1543 bool required_escaping = false;
1544 if (!raw_json) Append<uint8_t, DestChar>('"');
1545 // We might be able to fit the whole escaped string in the current string
1546 // part, or we might need to allocate.
1547 base::Vector<const SrcChar> vector = string->GetCharVector<SrcChar>(no_gc);
1548 if V8_LIKELY (EscapedLengthIfCurrentPartFits(vector.size())) {
1549 NoExtendBuilder<DestChar> no_extend(
1550 reinterpret_cast<DestChar*>(part_ptr_) + current_index_,
1553 vector, &no_extend);
1554 } else {
1556 (string->IsFlat() &&
1558 size_t uncopied_src_index = 0; // Index of first char not copied yet.
1559 for (size_t i = 0; i < vector.size(); i++) {
1560 SrcChar c = vector.at(i);
1561 if (raw_json || DoNotEscape(c)) {
1562 continue;
1563 } else if (sizeof(SrcChar) != 1 &&
1564 base::IsInRange(c, static_cast<SrcChar>(0xD800),
1565 static_cast<SrcChar>(0xDFFF))) {
1566 // The current character is a surrogate.
1567 required_escaping = true;
1568 AppendSubstring(vector.data(), uncopied_src_index, i);
1569
1570 char double_to_radix_chars[kDoubleToRadixMaxChars];
1571 base::Vector<char> double_to_radix_buffer =
1572 base::ArrayVector(double_to_radix_chars);
1573 if (c <= 0xDBFF) {
1574 // The current character is a leading surrogate.
1575 if (i + 1 < vector.size()) {
1576 // There is a next character.
1577 SrcChar next = vector.at(i + 1);
1578 if (base::IsInRange(next, static_cast<SrcChar>(0xDC00),
1579 static_cast<SrcChar>(0xDFFF))) {
1580 // The next character is a trailing surrogate, meaning this is a
1581 // surrogate pair.
1584 i++;
1585 } else {
1586 // The next character is not a trailing surrogate. Thus, the
1587 // current character is a lone leading surrogate.
1588 AppendCStringLiteral("\\u");
1589 std::string_view hex =
1590 DoubleToRadixStringView(c, 16, double_to_radix_buffer);
1591 AppendString(hex);
1592 }
1593 } else {
1594 // There is no next character. Thus, the current character is a
1595 // lone leading surrogate.
1596 AppendCStringLiteral("\\u");
1597 std::string_view hex =
1598 DoubleToRadixStringView(c, 16, double_to_radix_buffer);
1599 AppendString(hex);
1600 }
1601 } else {
1602 // The current character is a lone trailing surrogate. (If it had
1603 // been preceded by a leading surrogate, we would've ended up in the
1604 // other branch earlier on, and the current character would've been
1605 // handled as part of the surrogate pair already.)
1606 AppendCStringLiteral("\\u");
1607 std::string_view hex =
1608 DoubleToRadixStringView(c, 16, double_to_radix_buffer);
1609 AppendString(hex);
1610 }
1611 uncopied_src_index = i + 1;
1612 } else {
1613 required_escaping = true;
1614 AppendSubstring(vector.data(), uncopied_src_index, i);
1615 DCHECK_LT(c, 0x60);
1616 AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
1617 uncopied_src_index = i + 1;
1618 }
1619 }
1620 AppendSubstring(vector.data(), uncopied_src_index, vector.size());
1621 }
1622 if (!raw_json) Append<uint8_t, DestChar>('"');
1623 return required_escaping;
1624}
1625
1626template <typename DestChar>
1629 ReadOnlyRoots roots(isolate_);
1630 if (key->map() != roots.internalized_one_byte_string_map()) {
1631 return false;
1632 }
1633 if (!key_cache_.Contains(key)) {
1634 return false;
1635 }
1636 size_t length = key->length();
1637 size_t copy_length = length;
1638 if constexpr (sizeof(DestChar) == 1) {
1639 // CopyChars has fast paths for small integer lengths, and is generally a
1640 // little faster if we round the length up to the nearest 4. This is still
1641 // within the bounds of the object on the heap, because object alignment is
1642 // never less than 4 for any build configuration.
1643 constexpr int kRounding = 4;
1644 static_assert(kRounding <= kObjectAlignment);
1645 copy_length = RoundUp(length, kRounding);
1646 }
1647 // Add three for the quote marks and colon, to determine how much output space
1648 // is needed. We might actually require a little less output space than this,
1649 // depending on how much rounding happened above, but it's more important to
1650 // compute the requirement quickly than to be precise.
1651 size_t required_length = copy_length + 3;
1652 if (!CurrentPartCanFit(required_length)) {
1653 return false;
1654 }
1655 NoExtendBuilder<DestChar> no_extend(
1656 reinterpret_cast<DestChar*>(part_ptr_) + current_index_, &current_index_);
1657 no_extend.Append('"');
1659 Cast<SeqOneByteString>(key)->GetChars(no_gc), copy_length);
1660 DCHECK_LE(reinterpret_cast<Address>(chars.end()),
1661 key.address() + key->Size());
1662#if DEBUG
1663 for (size_t i = 0; i < length; ++i) {
1664 DCHECK(DoNotEscape(chars[i]));
1665 }
1666#endif // DEBUG
1667 no_extend.AppendChars(chars, length);
1668 no_extend.Append('"');
1669 no_extend.Append(':');
1670 return true;
1671}
1672
1674 if (gap_ == nullptr) return;
1676}
1677
1679 AppendCharacter('\n');
1680 for (int i = 0; i < indent_; i++) AppendCString(gap_);
1681}
1682
1684 if (!first) AppendCharacter(',');
1685 NewLine();
1686}
1687
1689 Handle<Object> deferred_key) {
1690 Separator(!deferred_comma);
1691 Handle<String> string_key = Cast<String>(deferred_key);
1692 bool wrote_simple = false;
1693 {
1695 wrote_simple =
1697 ? TrySerializeSimplePropertyKey<uint8_t>(*string_key, no_gc)
1698 : TrySerializeSimplePropertyKey<base::uc16>(*string_key, no_gc);
1699 }
1700
1701 if (!wrote_simple) {
1702 bool required_escaping = SerializeString<false>(string_key);
1703 if (!required_escaping) {
1704 key_cache_.TryInsert(*string_key);
1705 }
1706 AppendCharacter(':');
1707 }
1708
1709 if (gap_ != nullptr) AppendCharacter(' ');
1710}
1711
1712template <bool raw_json>
1714 object = String::Flatten(isolate_, object);
1716 auto string = *object;
1720 } else {
1722 }
1723 }
1727 } else {
1729 }
1730}
1731
1734 // Set the flag and carry on. Delay throwing the exception till the end.
1735 current_index_ = 0;
1736 overflowed_ = true;
1737 return;
1738 }
1741 uint8_t* tmp_ptr = new uint8_t[part_length_];
1742 memcpy(tmp_ptr, one_byte_ptr_, current_index_);
1744 one_byte_ptr_ = tmp_ptr;
1746 } else {
1747 base::uc16* tmp_ptr = new base::uc16[part_length_];
1748 for (uint32_t i = 0; i < current_index_; i++) {
1749 tmp_ptr[i] = two_byte_ptr_[i];
1750 }
1751 delete[] two_byte_ptr_;
1752 two_byte_ptr_ = tmp_ptr;
1754 }
1755}
1756
1760 for (uint32_t i = 0; i < current_index_; i++) {
1762 }
1765 one_byte_ptr_ = nullptr;
1766}
1767
1768template <typename Char>
1770 public:
1771 explicit OutBuffer(AccountingAllocator* allocator) : allocator_(allocator) {
1774 }
1775 template <typename SrcChar>
1776 requires(sizeof(Char) >= sizeof(SrcChar))
1777 V8_INLINE void AppendCharacter(SrcChar c) {
1780 *cur_++ = c;
1781 }
1782 template <typename SrcChar>
1783 requires(sizeof(Char) >= sizeof(SrcChar))
1784 void Append(const SrcChar* chars, size_t length) {
1785 ReduceCurrentCapacity(length);
1786 DCHECK_GE(SegmentFreeChars(), length);
1787 CopyChars(cur_, chars, length);
1788 cur_ += length;
1789 }
1790 void EnsureCapacity(size_t size) {
1791#ifdef DEBUG
1792 current_requested_capacity_ = size;
1793#endif
1794 if (V8_LIKELY(size <= SegmentFreeChars())) return;
1795 Extend(size);
1797 }
1798 size_t length() const {
1799 if (ZoneUsed()) {
1800 DCHECK_GT(segments_->length(), 0);
1801 size_t length = stack_buffer_size_;
1802 for (int i = 0; i < segments_->length() - 1; i++) {
1803 length += segments_->at(i).size();
1804 }
1805 length += CurSegmentLength();
1806 return length;
1807 } else {
1808 return StackBufferLength();
1809 }
1810 }
1811 template <typename Dst>
1812 void CopyTo(Dst* dst) {
1813 if (ZoneUsed()) {
1814 // Copy stack segment.
1816 dst += stack_buffer_size_;
1817 // Copy full segments.
1818 DCHECK_GT(segments_->length(), 0);
1819 for (int i = 0; i < segments_->length() - 1; i++) {
1820 base::Vector<Char> segment = segments_.value()[i];
1821 size_t segment_length = segment.size();
1822 CopyChars(dst, segment.begin(), segment_length);
1823 dst += segment_length;
1824 }
1825 // Copy last (partially filled) segment.
1826 base::Vector<Char> segment = segments_->last();
1827 CopyChars(dst, segment.begin(), CurSegmentLength());
1828 } else {
1829 // Copy (partially filled) stack segment.
1831 }
1832 }
1833
1834 private:
1835 static constexpr uint32_t kInitialSegmentSize = 2 * KB;
1836 static constexpr uint32_t kMaxSegmentSize = 32 * KB;
1839 static constexpr uint8_t kInitialSegmentSizeHighestBit =
1841 static constexpr uint8_t kMaxSegmentSizeHighestBit =
1843 static constexpr uint8_t kNumVariableSegments =
1845 static constexpr size_t kStackBufferSize = 256;
1846
1847 V8_NOINLINE V8_PRESERVE_MOST void Extend(size_t min_size) {
1848 if (ZoneUsed()) {
1849 segments_->last().Truncate(CurSegmentLength());
1850 } else {
1853 segments_.emplace(1, &zone_.value());
1854 }
1855 const size_t new_segment_size =
1856 std::max(min_size, SegmentCapacity(segments_->length()));
1857 segments_->Add(zone_->AllocateVector<Char>(new_segment_size),
1858 &zone_.value());
1859 cur_ = segments_->last().begin();
1860 segment_end_ = segments_->last().end();
1861 }
1862 V8_INLINE size_t SegmentFreeChars() const { return segment_end_ - cur_; }
1864 DCHECK(!ZoneUsed());
1865 return cur_ - stack_buffer_;
1866 }
1868 DCHECK(ZoneUsed());
1869 return cur_ - segments_->last().begin();
1870 }
1871 V8_INLINE size_t SegmentCapacity(size_t segment) {
1872 return 1u << std::min<size_t>(segment + kInitialSegmentSizeHighestBit,
1874 }
1876 DCHECK(ZoneUsed());
1877 DCHECK_GT(segments_->length(), 0);
1878 return segments_->last().size();
1879 }
1881#ifdef DEBUG
1882 DCHECK_LE(size, current_requested_capacity_);
1883 current_requested_capacity_ -= size;
1884#endif
1885 }
1886 V8_INLINE bool ZoneUsed() const { return zone_.has_value(); }
1887
1891 Char* cur_;
1893 std::optional<Zone> zone_;
1894 std::optional<ZoneList<base::Vector<Char>>> segments_;
1895#ifdef DEBUG
1896 size_t current_requested_capacity_;
1897#endif
1898};
1899
1909
1911 kSuccess,
1914};
1915
1916enum class ResumeJSObjectMode : uint8_t {
1920};
1921
1923 public:
1924 enum Type {
1934 kSimpleObject, // Resume after encoding change.
1935 // Object is any simple object that can be serialized
1936 // successfully with TrySerializeSimpleObject().
1937 kObjectKey // For encoding changes triggered by object keys.
1938 // This is required (instead of simply resuming with the
1939 // object/index that triggered the change), to avoid
1940 // re-serializing '{' if the first property key triggered
1941 // the change.
1943
1945
1947 return ContinuationRecord(Type::kSimpleObject, obj, 0, 0);
1948 }
1954 DCHECK(Is<JSArray>(obj));
1955 return ContinuationRecord(Type::kArray, obj, 0, 0);
1956 }
1957 template <ElementsKind kind, bool with_interrupt_check>
1959 Tagged<FixedArrayBase> obj, uint32_t index, uint32_t length) {
1960 return ContinuationRecord(
1961 ContinuationTypeForArray(kind, with_interrupt_check), obj, index,
1962 length);
1963 }
1965 DCHECK(Is<JSObject>(obj));
1966 return ContinuationRecord(Type::kObject, obj, Tagged<Map>(), 0, 0, 0, 0,
1968 }
1969 template <ResumeJSObjectMode mode>
1995
1996 Type type() const { return type_; }
1997 Tagged<ObjectT> object() const { return object_; }
2004 return Cast<JSArray>(object_);
2005 }
2018
2019 uint32_t array_index() const {
2021 return js_array_.index;
2022 }
2023 uint32_t array_length() const {
2025 return js_array_.length;
2026 }
2027
2028 uint16_t object_descriptor_idx() const {
2030 return js_object_.descriptor_idx;
2031 }
2032 uint16_t object_nof_descriptors() const {
2034 return js_object_.nof_descriptors;
2035 }
2038 return js_object_.in_object_properties;
2039 }
2042 return js_object_.in_object_properties_start;
2043 }
2046 return js_object_.descriptors;
2047 }
2050 return js_object_.map;
2051 }
2052
2053 bool object_key_comma() const {
2055 return object_key_.comma;
2056 }
2057
2058 static constexpr bool IsObjectResumeType(Type type) {
2059 return type == Type::kObjectResume_WithMapCache ||
2062 }
2063 static constexpr bool IsArrayResumeType(Type type) {
2064 return type == Type::kArrayResume || type == Type::kArrayResume_Holey ||
2067 }
2068
2069 private:
2070 constexpr ContinuationRecord(Type type, Tagged<ObjectT> obj, uint32_t index,
2071 uint32_t length)
2072 : type_(type), object_(obj), js_array_({index, length}) {}
2084 : type_(type), object_(obj), object_key_{comma} {}
2085
2089 static_assert(JS_OBJECT - 1 == Type::kObject);
2090 static_assert(JS_ARRAY - 1 == Type::kArray);
2091 return static_cast<Type>(result - 1);
2092 }
2093
2095 bool with_interrupt_check) {
2098 if (with_interrupt_check) {
2100 } else {
2101 return kArrayResume_Holey;
2102 }
2103 } else {
2104 if (with_interrupt_check) {
2106 } else {
2107 return kArrayResume;
2108 }
2109 }
2110 }
2111
2114 union {
2115 struct {
2116 uint32_t index;
2117 uint32_t length;
2119 struct {
2127 struct {
2128 bool comma;
2130 };
2131};
2132
2133template <typename Char>
2135 public:
2136 explicit FastJsonStringifier(Isolate* isolate);
2137
2138 size_t ResultLength() const { return buffer_.length(); }
2139 template <typename DstChar>
2140 void CopyResultTo(DstChar* out_buffer) {
2141 buffer_.CopyTo(out_buffer);
2142 }
2145
2146 template <typename OldChar>
2147 requires(sizeof(OldChar) < sizeof(Char))
2150 const DisallowGarbageCollection& no_gc);
2151
2152 private:
2153 static constexpr bool is_one_byte = sizeof(Char) == sizeof(uint8_t);
2154
2156 if (comma) AppendCharacterUnchecked(',');
2157 }
2158 V8_INLINE void Separator(bool comma) {
2159 if (comma) AppendCharacter(',');
2160 }
2161 V8_INLINE void SerializeSmi(Tagged<Smi> object);
2162 void SerializeDouble(double number);
2163 template <bool no_escaping>
2165 Tagged<String> key, bool comma, const DisallowGarbageCollection& no_gc);
2166 template <typename StringT, bool no_escaping>
2168 Tagged<String> key, bool comma, const DisallowGarbageCollection& no_gc);
2169 template <typename StringT>
2172
2180 template <ResumeJSObjectMode mode>
2182 Tagged<JSObject> obj, Tagged<Map> map, uint16_t start_descriptor_idx,
2183 uint16_t nof_descriptors, uint8_t in_object_properties,
2184 uint8_t in_object_properties_start, Tagged<DescriptorArray> descriptors,
2185 bool comma, const DisallowGarbageCollection& no_gc);
2187 template <ElementsKind kind>
2189 Tagged<FixedArrayBase> elements, uint32_t start_index, uint32_t length);
2190 template <ElementsKind kind>
2192 Tagged<FixedArrayBase> array, uint32_t start_idx, uint32_t length);
2193 template <ElementsKind kind, bool with_interrupt_checks, typename T>
2195 SerializeFixedArrayElement(Tagged<T> elements, uint32_t i, uint32_t length);
2196
2198 V8_NOINLINE bool CheckCycle();
2199
2200 V8_INLINE void EnsureCapacity(size_t size) { buffer_.EnsureCapacity(size); }
2201 template <typename SrcChar>
2203 buffer_.AppendCharacter(c);
2204 }
2205 template <typename SrcChar>
2206 V8_INLINE void AppendCharacter(SrcChar c) {
2207 EnsureCapacity(1);
2209 }
2210 template <size_t N>
2212 // Note that the literal contains the zero char.
2213 constexpr size_t length = N - 1;
2214 static_assert(length > 0);
2215 if constexpr (length == 1) return AppendCharacterUnchecked(literal[0]);
2216 const uint8_t* chars = reinterpret_cast<const uint8_t*>(literal);
2217 buffer_.Append(chars, length);
2218 }
2219 template <size_t N>
2221 // Note that the literal contains the zero char.
2222 constexpr size_t length = N - 1;
2223 static_assert(length > 0);
2224 EnsureCapacity(length);
2226 }
2227
2228 V8_INLINE void AppendCStringUnchecked(const char* chars, size_t len) {
2229 buffer_.Append(reinterpret_cast<const unsigned char*>(chars), len);
2230 }
2231 V8_INLINE void AppendCStringUnchecked(const char* chars) {
2232 AppendCStringUnchecked(chars, strlen(chars));
2233 }
2234 V8_INLINE void AppendStringUnchecked(std::string_view str) {
2235 AppendCStringUnchecked(str.data(), str.length());
2236 }
2237 V8_INLINE void AppendCString(const char* chars, size_t len) {
2238 EnsureCapacity(len);
2239 AppendCStringUnchecked(chars, len);
2240 }
2241 V8_INLINE void AppendCString(const char* chars) {
2242 AppendCString(chars, strlen(chars));
2243 }
2244 V8_INLINE void AppendString(std::string_view str) {
2245 AppendCString(str.data(), str.length());
2246 }
2247
2248 template <typename SrcChar>
2249 requires(sizeof(SrcChar) == sizeof(uint8_t))
2250 V8_INLINE bool AppendString(const SrcChar* chars, size_t length,
2251 const DisallowGarbageCollection& no_gc);
2252
2253 template <typename SrcChar>
2254 V8_INLINE void AppendStringNoEscapes(const SrcChar* chars, size_t length,
2255 const DisallowGarbageCollection& no_gc);
2256
2257 template <typename SrcChar>
2258 requires(sizeof(SrcChar) == sizeof(uint8_t))
2259 bool AppendStringScalar(const SrcChar* chars, size_t length, size_t start,
2260 size_t uncopied_src_index,
2261 const DisallowGarbageCollection& no_gc);
2262
2263 template <typename SrcChar>
2264 requires(sizeof(SrcChar) == sizeof(uint8_t))
2265 V8_INLINE bool AppendStringSWAR(const SrcChar* chars, size_t length,
2266 size_t start, size_t uncopied_src_index,
2267 const DisallowGarbageCollection& no_gc);
2268
2269 template <typename SrcChar>
2270 requires(sizeof(SrcChar) == sizeof(uint8_t))
2271 V8_INLINE bool AppendStringSIMD(const SrcChar* chars, size_t length,
2272 const DisallowGarbageCollection& no_gc);
2273
2274 template <typename SrcChar>
2275 requires(sizeof(SrcChar) == sizeof(base::uc16))
2276 V8_INLINE bool AppendString(const SrcChar* chars, size_t length,
2277 const DisallowGarbageCollection& no_gc);
2278
2279 static constexpr uint32_t kGlobalInterruptBudget = 200000;
2280 static constexpr uint32_t kArrayInterruptLength = 4000;
2281
2286
2289 // Contains all JSObject maps for which we have seen all object keys already.
2290 // The value is true for a map iff all of the following conditions are true:
2291 // * No property key is a symbol.
2292 // * All property keys are enumberable.
2293 // * No property key contains any character that requires escaping.
2295 template <typename>
2297};
2298
2299namespace {
2300
2301V8_INLINE bool CanFastSerializeJSArrayFastPath(Tagged<JSArray> object,
2302 Tagged<HeapObject> initial_proto,
2303 Isolate* isolate) {
2304 // Check if the prototype is the initial array prototype without interesting
2305 // properties (toJSON).
2306 Tagged<Map> map = object->map();
2307 if (V8_UNLIKELY(map->may_have_interesting_properties())) return false;
2308 Tagged<HeapObject> proto = map->prototype();
2309 // Note: This will also fail for sub-objects in different native contexts.
2310 // This should be rare and bailing-out to the slow-path is fine.
2311 return V8_LIKELY(proto == initial_proto);
2312}
2313
2314V8_INLINE bool CanFastSerializeJSObjectFastPath(
2315 Tagged<JSObject> object, Tagged<HeapObject> initial_proto, Tagged<Map> map,
2316 Isolate* isolate) {
2317 if (V8_UNLIKELY(IsCustomElementsReceiverMap(map))) return false;
2318 if (V8_UNLIKELY(!object->HasFastProperties())) return false;
2319 auto roots = ReadOnlyRoots(isolate);
2320 auto elements = object->elements();
2321 if (V8_UNLIKELY(elements != roots.empty_fixed_array() &&
2322 elements != roots.empty_slow_element_dictionary())) {
2323 return false;
2324 }
2325 if (V8_UNLIKELY(map->may_have_interesting_properties())) return false;
2326 Tagged<HeapObject> proto = map->prototype();
2327 // Note: This will also fail for sub-objects in different native contexts.
2328 // This should be rare and bailing-out to the slow-path is fine.
2329 return V8_LIKELY(proto == initial_proto);
2330}
2331
2332} // namespace
2333
2334template <typename Char>
2336 : isolate_(isolate),
2337 zone_(isolate->allocator(), kJsonStringifierZoneName),
2338 buffer_(isolate->allocator()),
2339 map_cache_(&zone_) {}
2340
2341template <typename Char>
2343 static_assert(Smi::kMaxValue <= 2147483647);
2344 static_assert(Smi::kMinValue >= -2147483648);
2345 // sizeof(string) includes \0.
2346 static constexpr uint32_t kBufferSize = sizeof("-2147483648") - 1;
2347 char chars[kBufferSize];
2348 base::Vector<char> buffer(chars, kBufferSize);
2349 std::string_view str = IntToStringView(object.value(), buffer);
2350 AppendString(str);
2351}
2352
2353template <typename Char>
2355 if (V8_UNLIKELY(std::isinf(number) || std::isnan(number))) {
2356 AppendCStringLiteral("null");
2357 return;
2358 }
2359 static constexpr uint32_t kBufferSize = 100;
2360 char chars[kBufferSize];
2361 base::Vector<char> buffer(chars, kBufferSize);
2362 std::string_view str = DoubleToStringView(number, buffer);
2363 AppendString(str);
2364}
2365
2366template <typename Char>
2367template <bool no_escaping>
2370 Tagged<String> key, bool comma, const DisallowGarbageCollection& no_gc) {
2371#if V8_STATIC_ROOTS_BOOL
2372 // This is slightly faster than the switch over instance types.
2373 ReadOnlyRoots roots(isolate_);
2374 Tagged<Map> map = key->map();
2375 if (map == roots.internalized_one_byte_string_map()) {
2376 V8_INLINE_STATEMENT return SerializeObjectKey<SeqOneByteString,
2377 no_escaping>(key, comma,
2378 no_gc);
2379 } else if (map == roots.external_internalized_one_byte_string_map() ||
2380 map ==
2381 roots.uncached_external_internalized_one_byte_string_map()) {
2382 V8_INLINE_STATEMENT return SerializeObjectKey<ExternalOneByteString,
2383 no_escaping>(key, comma,
2384 no_gc);
2385 } else {
2386 if constexpr (is_one_byte) {
2388 // no_escaping is only possible if we have already seen all the keys in a
2389 // map. But it is not possible we have seen a two-byte string and are
2390 // still in one-byte mode.
2391 if constexpr (no_escaping) {
2392 UNREACHABLE();
2393 } else {
2395 }
2396 } else {
2397 if (map == roots.internalized_two_byte_string_map()) {
2398 return SerializeObjectKey<SeqTwoByteString, no_escaping>(key, comma,
2399 no_gc);
2400 } else if (
2401 map == roots.external_internalized_two_byte_string_map() ||
2402 map == roots.uncached_external_internalized_two_byte_string_map()) {
2403 return SerializeObjectKey<ExternalTwoByteString, no_escaping>(
2404 key, comma, no_gc);
2405 }
2406 }
2407 }
2408#else
2409 InstanceType instance_type = key->map()->instance_type();
2410 switch (instance_type) {
2412 V8_INLINE_STATEMENT return SerializeObjectKey<SeqOneByteString,
2413 no_escaping>(key, comma,
2414 no_gc);
2417 V8_INLINE_STATEMENT return SerializeObjectKey<ExternalOneByteString,
2418 no_escaping>(key, comma,
2419 no_gc);
2421 return SerializeObjectKey<SeqTwoByteString, no_escaping>(key, comma,
2422 no_gc);
2425 return SerializeObjectKey<ExternalTwoByteString, no_escaping>(key, comma,
2426 no_gc);
2427 default:
2428 UNREACHABLE();
2429 }
2430#endif
2431 UNREACHABLE();
2432}
2433
2434namespace {
2435
2436// The worst case length of an escaped character is 6. Shifting the string
2437// length left by 3 is a more pessimistic estimate, but faster to calculate.
2438size_t MaxEscapedStringLength(uint32_t length) { return length << 3; }
2439
2440} // namespace
2441
2442template <typename Char>
2443template <typename StringT, bool no_escaping>
2446 Tagged<String> obj, bool comma, const DisallowGarbageCollection& no_gc) {
2447 using StringChar = StringT::Char;
2448 if constexpr (is_one_byte && sizeof(StringChar) == 2) {
2449 // no_escaping is only possible if we have already seen all the keys in a
2450 // map. But it is not possible we have seen a two-byte string and are still
2451 // in one-byte mode.
2452 if constexpr (no_escaping) {
2453 UNREACHABLE();
2454 } else {
2456 }
2457 } else {
2458 Tagged<StringT> string = Cast<StringT>(obj);
2459 const StringChar* chars;
2460 if constexpr (requires { string->GetChars(no_gc); }) {
2461 chars = string->GetChars(no_gc);
2462 } else {
2463 chars = string->GetChars();
2464 }
2465 const uint32_t length = string->length();
2466 size_t max_length;
2467 if constexpr (no_escaping) {
2468 max_length = length;
2469 } else {
2470 max_length = MaxEscapedStringLength(length);
2471 }
2472 max_length += 4 /* optional comma + 2x double quote + colon */;
2473 EnsureCapacity(max_length);
2474 SeparatorUnchecked(comma);
2475 AppendCharacterUnchecked('"');
2477 if constexpr (no_escaping) {
2478 AppendStringNoEscapes(chars, length, no_gc);
2480 } else {
2481 bool needs_escaping = AppendString(chars, length, no_gc);
2482 result = needs_escaping
2485 }
2486 AppendCharacterUnchecked('"');
2487 AppendCharacterUnchecked(':');
2488 return result;
2489 }
2490}
2491
2492template <typename Char>
2493template <typename StringT>
2496 using StringChar = StringT::Char;
2497 if constexpr (is_one_byte && sizeof(StringChar) == 2) {
2498 return CHANGE_ENCODING;
2499 } else {
2500 Tagged<StringT> string = Cast<StringT>(obj);
2501 const StringChar* chars;
2502 if constexpr (requires { string->GetChars(no_gc); }) {
2503 chars = string->GetChars(no_gc);
2504 } else {
2505 chars = string->GetChars();
2506 }
2507 const uint32_t length = string->length();
2508 EnsureCapacity(MaxEscapedStringLength(length) + 2 /* 2x double quote */);
2509 AppendCharacterUnchecked('"');
2510 AppendString(chars, length, no_gc);
2511 AppendCharacterUnchecked('"');
2512 return SUCCESS;
2513 }
2514}
2515
2516template <typename Char>
2518 Tagged<JSAny> object) {
2520 // GCMole doesn't handle kNoGC interrupts correctly.
2521 DisableGCMole no_gc_mole;
2522
2523 if (IsSmi(object)) {
2524 SerializeSmi(Cast<Smi>(object));
2525 return SUCCESS;
2526 }
2528 Tagged<Map> map = obj->map();
2529 InstanceType instance_type = map->instance_type();
2530 switch (instance_type) {
2533 return SerializeString<SeqOneByteString>(obj, no_gc);
2538 return SerializeString<ExternalOneByteString>(obj, no_gc);
2540 Tagged<String> actual = Cast<ThinString>(obj)->actual();
2541 if (IsExternalString(actual)) {
2542 return SerializeString<ExternalOneByteString>(actual, no_gc);
2543 } else {
2544 return SerializeString<SeqOneByteString>(actual, no_gc);
2545 }
2546 }
2549 return SerializeString<SeqTwoByteString>(obj, no_gc);
2554 return SerializeString<ExternalTwoByteString>(obj, no_gc);
2556 if constexpr (is_one_byte) {
2557 return CHANGE_ENCODING;
2558 } else {
2559 Tagged<String> actual = Cast<ThinString>(obj)->actual();
2560 if (IsExternalString(actual)) {
2561 return SerializeString<ExternalTwoByteString>(actual, no_gc);
2562 } else {
2563 return SerializeString<SeqTwoByteString>(actual, no_gc);
2564 }
2565 }
2566 }
2567 case HEAP_NUMBER_TYPE:
2568 SerializeDouble(Cast<HeapNumber>(obj)->value());
2569 return SUCCESS;
2570 case ODDBALL_TYPE:
2571 switch (Cast<Oddball>(obj)->kind()) {
2572 case Oddball::kFalse:
2573 AppendCStringLiteral("false");
2574 return SUCCESS;
2575 case Oddball::kTrue:
2576 AppendCStringLiteral("true");
2577 return SUCCESS;
2578 case Oddball::kNull:
2579 AppendCStringLiteral("null");
2580 return SUCCESS;
2581 default:
2582 return UNDEFINED;
2583 }
2584 case SYMBOL_TYPE:
2585 return UNDEFINED;
2586 case JS_PRIMITIVE_WRAPPER_TYPE:
2587 return SerializeJSPrimitiveWrapper(Cast<JSPrimitiveWrapper>(obj), no_gc);
2588 case JS_OBJECT_TYPE:
2589 return JS_OBJECT;
2590 case JS_ARRAY_TYPE:
2591 return JS_ARRAY;
2592 default:
2593 return SLOW_PATH;
2594 }
2595
2596 UNREACHABLE();
2597}
2598
2599namespace {
2600
2601Builtin GetBuiltin(Isolate* isolate, Tagged<JSObject> obj,
2602 DirectHandle<Name> name) {
2603 HandleScope handle_scope(isolate);
2604
2605 DirectHandle<JSObject> obj_handle = direct_handle(obj, isolate);
2606 LookupIterator it(isolate, obj_handle, name, obj_handle);
2607 if (!it.IsFound()) {
2608 return Builtin::kNoBuiltinId;
2609 }
2610 if (it.state() != LookupIterator::DATA) {
2611 return Builtin::kNoBuiltinId;
2612 }
2613 DirectHandle<Object> fun = Object::GetProperty(&it).ToHandleChecked();
2614 if (!IsJSFunction(*fun)) {
2615 return Builtin::kNoBuiltinId;
2616 }
2617 return Cast<JSFunction>(*fun)->code(isolate)->builtin_id();
2618}
2619
2620} // namespace
2621
2622template <typename Char>
2626 // TODO(pthier): Consider extending IsStringWrapperToPrimitive to also cover
2627 // toString() and all JSPrimitiveWrappers to avoid some of the checks here.
2629 return SLOW_PATH;
2630 }
2631
2632 Tagged<JSAny> raw = obj->value();
2633 if (IsSymbol(raw)) {
2634 // Symbol object wrapper is treated as a regular object.
2635 Tagged<Map> map = obj->map();
2636 // Check that object has no own properties.
2637 if (V8_UNLIKELY(!obj->HasFastProperties())) return SLOW_PATH;
2638 if (V8_UNLIKELY(map->NumberOfOwnDescriptors() != 0)) return SLOW_PATH;
2639 // Check that object has no elements.
2640 auto roots = ReadOnlyRoots(isolate_);
2641 auto elements = obj->elements();
2642 if (V8_UNLIKELY(elements != roots.empty_fixed_array() &&
2643 elements != roots.empty_slow_element_dictionary())) {
2644 return SLOW_PATH;
2645 }
2646
2647 AppendCStringLiteral("{}");
2648 return SUCCESS;
2649 } else if (IsString(raw)) {
2650 if (V8_UNLIKELY(!Protectors::IsStringWrapperToPrimitiveIntact(isolate_))) {
2651 return SLOW_PATH;
2652 }
2653 // We only fetch data properties in GetBuiltin, but GCMole doesn't know
2654 // that and assumes GCs can happen.
2655 DisableGCMole no_gc_mole;
2656 // Check that toString() on the prototype chain is the expected builtin.
2657 if (V8_UNLIKELY(
2658 GetBuiltin(isolate_, obj, isolate_->factory()->toString_string()) !=
2659 Builtin::kStringPrototypeToString)) {
2660 return SLOW_PATH;
2661 }
2662
2663 Tagged<String> string = Cast<String>(raw);
2664 while (true) {
2666 string->DispatchToSpecificType(base::overloaded{
2667 [&](Tagged<SeqOneByteString> str) {
2668 return SerializeString<SeqOneByteString>(str, no_gc);
2669 },
2670 [&](Tagged<SeqTwoByteString> str) {
2671 return SerializeString<SeqTwoByteString>(str, no_gc);
2672 },
2674 return SerializeString<ExternalOneByteString>(str, no_gc);
2675 },
2677 return SerializeString<ExternalTwoByteString>(str, no_gc);
2678 },
2679 [&](Tagged<ThinString> str) {
2680 string = str->actual();
2681 // UNDEFINED is misused here to indicate that we continue after
2682 // unwrapping.
2683 return UNDEFINED;
2684 },
2685 [&](Tagged<ConsString> str) { return SLOW_PATH; },
2686 [&](Tagged<SlicedString> str) { return SLOW_PATH; }});
2687 if (V8_LIKELY(result != UNDEFINED)) return result;
2688 }
2689 UNREACHABLE();
2690 } else if (IsNumber(raw)) {
2691 // We only fetch data properties in GetBuiltin, but GCMole doesn't know
2692 // that and assumes GCs can happen.
2693 DisableGCMole no_gc_mole;
2694 // Check that valueOf() on the prototype chain is the expected builtin.
2695 if (V8_UNLIKELY(
2696 GetBuiltin(isolate_, obj, isolate_->factory()->valueOf_string()) !=
2697 Builtin::kNumberPrototypeValueOf)) {
2698 return SLOW_PATH;
2699 }
2700 if (IsSmi(raw)) {
2701 SerializeSmi(Cast<Smi>(raw));
2702 return SUCCESS;
2703 }
2704 DCHECK(IsHeapNumber(raw));
2705 SerializeDouble(Cast<HeapNumber>(raw)->value());
2706 return SUCCESS;
2707 } else if (IsBoolean(raw)) {
2708 if (IsTrue(raw, isolate_)) {
2709 AppendCStringLiteral("true");
2710 } else {
2711 AppendCStringLiteral("false");
2712 }
2713 return SUCCESS;
2714 } else if (IsBigInt(raw)) {
2715 return SLOW_PATH;
2716 }
2717
2718 UNREACHABLE();
2719}
2720
2721template <typename Char>
2723 Tagged<JSObject> obj, const DisallowGarbageCollection& no_gc) {
2724 Tagged<Map> map = obj->map();
2725 if (V8_UNLIKELY(!CanFastSerializeJSObjectFastPath(
2726 obj, initial_jsobject_proto_, map, isolate_))) {
2727 return SLOW_PATH;
2728 }
2729 AppendCharacter('{');
2730 const uint16_t nof_descriptors = map->NumberOfOwnDescriptors();
2731 const uint8_t in_object_properties = map->GetInObjectProperties();
2732 const uint8_t in_object_properties_start =
2733 map->GetInObjectPropertiesStartInWords();
2734 const Tagged<DescriptorArray> descriptors = map->instance_descriptors();
2735 auto mode = map_cache_.find(map);
2736 if (mode == map_cache_.end()) {
2737 return ResumeJSObject<ResumeJSObjectMode::kBuildingMapCache>(
2738 obj, map, 0, nof_descriptors, in_object_properties,
2739 in_object_properties_start, descriptors, false, no_gc);
2740 } else if (mode->second == true) {
2741 return ResumeJSObject<ResumeJSObjectMode::kWithMapCache>(
2742 obj, map, 0, nof_descriptors, in_object_properties,
2743 in_object_properties_start, descriptors, false, no_gc);
2744 } else {
2745 return ResumeJSObject<ResumeJSObjectMode::kWithoutMapCache>(
2746 obj, map, 0, nof_descriptors, in_object_properties,
2747 in_object_properties_start, descriptors, false, no_gc);
2748 }
2749}
2750
2751template <typename Char>
2752template <ResumeJSObjectMode mode>
2754 Tagged<JSObject> obj, Tagged<Map> map, uint16_t start_descriptor_idx,
2755 uint16_t nof_descriptors, uint8_t in_object_properties,
2756 uint8_t in_object_properties_start, Tagged<DescriptorArray> descriptors,
2757 bool comma, const DisallowGarbageCollection& no_gc) {
2759 InternalIndex::Range range{start_descriptor_idx, nof_descriptors};
2760 for (InternalIndex i : range) {
2761 // We don't deal with dictionary mode objects on the fast-path, so the index
2762 // is guaranteed to be less than uint16_t::max.
2763 static_assert(kMaxNumberOfDescriptors <
2764 std::numeric_limits<uint16_t>::max());
2765 DCHECK_LE(i.as_uint32(), kMaxNumberOfDescriptors);
2766 const uint16_t descriptor_idx = static_cast<uint16_t>(i.as_uint32());
2767
2768 Tagged<Name> name = descriptors->GetKey(i);
2769 int property_index;
2770 if constexpr (mode != ResumeJSObjectMode::kWithMapCache) {
2771 if (V8_UNLIKELY(IsSymbol(name))) {
2772 if constexpr (mode == ResumeJSObjectMode::kBuildingMapCache) {
2773 map_cache_[map] = false;
2774 }
2775 continue;
2776 }
2777 PropertyDetails details = descriptors->GetDetails(i);
2778 if (V8_UNLIKELY(details.IsDontEnum())) {
2779 if constexpr (mode == ResumeJSObjectMode::kBuildingMapCache) {
2780 map_cache_[map] = false;
2781 }
2782 continue;
2783 }
2784 if (V8_UNLIKELY(details.location() != PropertyLocation::kField)) {
2785 return SLOW_PATH;
2786 }
2788 property_index = details.field_index();
2789 } else {
2790 DCHECK_EQ(descriptor_idx, descriptors->GetDetails(i).field_index());
2791 property_index = descriptor_idx;
2792 }
2793 const bool is_inobject = property_index < in_object_properties;
2795 if (is_inobject) {
2796 int offset = (in_object_properties_start + property_index) * kTaggedSize;
2797 property = TaggedField<JSAny>::Relaxed_Load(cage_base, obj, offset);
2798 } else {
2799 property_index -= in_object_properties;
2800 property = obj->property_array(cage_base)->get(cage_base, property_index);
2801 }
2802
2803 if (V8_UNLIKELY(IsUndefined(property) || IsSymbol(property))) {
2804 continue;
2805 }
2806
2807 DCHECK(IsInternalizedString(name));
2808 Tagged<String> key_name = Cast<String>(name);
2810 if constexpr (mode == ResumeJSObjectMode::kWithMapCache) {
2811 V8_INLINE_STATEMENT key_result =
2812 SerializeObjectKey<true>(key_name, comma, no_gc);
2814 } else {
2815 key_result = SerializeObjectKey<false>(key_name, comma, no_gc);
2816 // We only need to check the result while building the map cache.
2817 // Once the map cache is built, we are sure that we won't observe any
2818 // encoding changes.
2819 // In addition we don't care if we need escaping or not, since we already
2820 // need to check for that unconditionally without a fast map cache.
2821 if constexpr (mode == ResumeJSObjectMode::kBuildingMapCache) {
2822 if (V8_UNLIKELY(key_result !=
2824 if constexpr (is_one_byte) {
2825 if (key_result ==
2828 obj, map, descriptor_idx + 1, nof_descriptors,
2829 in_object_properties, in_object_properties_start,
2830 descriptors));
2831 if (IsJSObject(property)) {
2832 stack_.emplace_back(ContinuationRecord::ForJSObject(property));
2833 } else if (IsJSArray(property)) {
2834 stack_.emplace_back(ContinuationRecord::ForJSArray(property));
2835 } else {
2836 stack_.emplace_back(
2838 }
2839 stack_.emplace_back(
2840 ContinuationRecord::ForObjectKey(key_name, comma));
2841 return CHANGE_ENCODING;
2842 }
2843 } else {
2844 DCHECK_NE(key_result,
2846 }
2847 if (key_result ==
2849 map_cache_[map] = false;
2850 }
2851 }
2852 }
2853 }
2854 // TrySerializeSimpleObject won't trigger GCs. See DisableGCMole scopes in
2855 // SerializeJSPrimitiveWrapper for explanation.
2856 DisableGCMole no_gc_mole;
2858 if constexpr (mode == ResumeJSObjectMode::kWithMapCache) {
2859 V8_INLINE_STATEMENT result = TrySerializeSimpleObject(property);
2860 } else {
2861 result = TrySerializeSimpleObject(property);
2862 }
2863 switch (result) {
2864 case SUCCESS:
2865 comma = true;
2866 break;
2867 case UNDEFINED:
2868 break;
2869 case JS_OBJECT:
2870 case JS_ARRAY:
2872 obj, map, descriptor_idx + 1, nof_descriptors, in_object_properties,
2873 in_object_properties_start, descriptors));
2874 stack_.push_back(ContinuationRecord::ForJSAny(property, result));
2875 return result;
2876 case CHANGE_ENCODING:
2877 if constexpr (is_one_byte) {
2879 obj, map, descriptor_idx + 1, nof_descriptors,
2880 in_object_properties, in_object_properties_start, descriptors));
2881 stack_.push_back(ContinuationRecord::ForSimpleObject(property));
2882 return result;
2883 } else {
2884 UNREACHABLE();
2885 }
2886 case SLOW_PATH:
2887 case EXCEPTION:
2888 return result;
2889 }
2890 }
2891 AppendCharacter('}');
2892 if constexpr (mode == ResumeJSObjectMode::kBuildingMapCache) {
2893 map_cache_.try_emplace(map, true);
2894 }
2895 return SUCCESS;
2896}
2897
2898template <typename Char>
2900 Tagged<JSArray> array) {
2901 if (V8_UNLIKELY(!CanFastSerializeJSArrayFastPath(
2902 array, initial_jsarray_proto_, isolate_))) {
2903 return SLOW_PATH;
2904 }
2905 AppendCharacter('[');
2906 uint32_t length = static_cast<uint32_t>(Object::NumberValue(array->length()));
2907 Tagged<FixedArrayBase> elements = array->elements();
2908 switch (array->GetElementsKind()) {
2909#define CASE(kind) \
2910 case kind: \
2911 if constexpr (IsHoleyElementsKind(kind)) { \
2912 if (V8_UNLIKELY(!Protectors::IsNoElementsIntact(isolate_))) { \
2913 return SLOW_PATH; \
2914 } \
2915 } \
2916 if (V8_UNLIKELY(length > kArrayInterruptLength)) { \
2917 return SerializeFixedArrayWithInterruptCheck<kind>(elements, 0, length); \
2918 } else { \
2919 return SerializeFixedArray<kind>(elements, 0, length); \
2920 }
2927#undef CASE
2928 default:
2929 return SLOW_PATH;
2930 }
2931 UNREACHABLE();
2932}
2933
2934template <typename Char>
2935template <ElementsKind kind>
2938 Tagged<FixedArrayBase> elements, uint32_t start_index, uint32_t length) {
2939 using ArrayT = std::conditional_t<IsDoubleElementsKind(kind),
2941
2942 StackLimitCheck interrupt_check(isolate_);
2943 uint32_t limit = std::min(length, kArrayInterruptLength);
2944 constexpr uint32_t kMaxAllowedFastPackedLength =
2945 std::numeric_limits<uint32_t>::max() - kArrayInterruptLength;
2946 static_assert(FixedArray::kMaxLength < kMaxAllowedFastPackedLength);
2947
2948 // GCMole doesn't handle kNoGC interrupts correctly.
2949 DisableGCMole no_gc_mole;
2950
2951 uint32_t i = start_index;
2952 while (true) {
2953 for (; i < limit; i++) {
2954 FastJsonStringifierResult result = SerializeFixedArrayElement<kind, true>(
2955 Cast<ArrayT>(elements), i, length);
2956 if (result != SUCCESS) return result;
2957 }
2958 if (i >= length) {
2959 AppendCharacter(']');
2960 return SUCCESS;
2961 }
2962 DCHECK_LT(limit, kMaxAllowedFastPackedLength);
2963 limit = std::min(length, limit + kArrayInterruptLength);
2964 {
2965 // A TerminationException can actually trigger a GC when allocating the
2966 // message object. We don't touch any of the heap objects after we
2967 // encountered an exception, so this is fine.
2968 AllowGarbageCollection allow_gc;
2969 if (interrupt_check.InterruptRequested() &&
2970 IsException(isolate_->stack_guard()->HandleInterrupts(
2972 isolate_)) {
2973 return EXCEPTION;
2974 }
2975 }
2976 }
2977 UNREACHABLE();
2978}
2979
2980template <typename Char>
2981template <ElementsKind kind>
2983 Tagged<FixedArrayBase> elements, uint32_t start_index, uint32_t length) {
2984 using ArrayT = std::conditional_t<IsDoubleElementsKind(kind),
2986
2987 for (uint32_t i = start_index; i < length; i++) {
2988 FastJsonStringifierResult result = SerializeFixedArrayElement<kind, false>(
2989 Cast<ArrayT>(elements), i, length);
2990 if (result != SUCCESS) return result;
2991 }
2992 AppendCharacter(']');
2993 return SUCCESS;
2994}
2995
2996template <typename Char>
2997template <ElementsKind kind, bool with_interrupt_checks, typename T>
2999 Tagged<T> elements, uint32_t i, uint32_t length) {
3000 if constexpr (IsHoleyElementsKind(kind)) {
3001 if (elements->is_the_hole(isolate_, i)) {
3002 EnsureCapacity(5 /* "null" + optional comma */);
3003 SeparatorUnchecked(i > 0);
3004 AppendCStringLiteralUnchecked("null");
3005 return SUCCESS;
3006 }
3007 }
3008 DCHECK(!elements->is_the_hole(isolate_, i));
3009 Separator(i > 0);
3010 if constexpr (IsSmiElementsKind(kind)) {
3011 SerializeSmi(Cast<Smi>(elements->get(i)));
3012 } else if constexpr (IsDoubleElementsKind(kind)) {
3013 SerializeDouble(elements->get_scalar(i));
3014 } else {
3015 Tagged<JSAny> obj = Cast<JSAny>(elements->get(i));
3016 // TrySerializeSimpleObject won't trigger GCs. See DisableGCMole scopes in
3017 // SerializeJSPrimitiveWrapper for explanation.
3018 DisableGCMole no_gc_mole;
3020 V8_INLINE_STATEMENT result = TrySerializeSimpleObject(obj);
3021 switch (result) {
3022 case UNDEFINED:
3023 AppendCStringLiteral("null");
3024 return SUCCESS;
3025 case CHANGE_ENCODING:
3026 if constexpr (is_one_byte) {
3027 DCHECK(IsString(obj) || IsStringWrapper(obj));
3028 stack_.push_back(
3030 elements, i + 1, length));
3031 stack_.push_back(ContinuationRecord::ForSimpleObject(obj));
3032 return result;
3033 } else {
3034 UNREACHABLE();
3035 }
3036 case JS_OBJECT:
3037 case JS_ARRAY:
3038 stack_.push_back(
3040 elements, i + 1, length));
3041 stack_.push_back(ContinuationRecord::ForJSAny(obj, result));
3042 return result;
3043 case SUCCESS:
3044 case SLOW_PATH:
3045 case EXCEPTION:
3046 return result;
3047 }
3048 UNREACHABLE();
3049 }
3050 return SUCCESS;
3051}
3052
3053template <typename Char>
3054template <typename OldChar>
3055 requires(sizeof(OldChar) < sizeof(Char))
3057 FastJsonStringifier<OldChar>& old_stringifier,
3058 const DisallowGarbageCollection& no_gc) {
3059 DCHECK_EQ(ResultLength(), 0);
3060 DCHECK(stack_.empty());
3061 DCHECK(!old_stringifier.stack_.empty());
3062
3063 initial_jsobject_proto_ = old_stringifier.initial_jsobject_proto_;
3064 initial_jsarray_proto_ = old_stringifier.initial_jsarray_proto_;
3065 stack_ = old_stringifier.stack_;
3066 ContinuationRecord cont = stack_.back();
3067 stack_.pop_back();
3068 if (cont.type() == ContinuationRecord::kObjectKey) {
3069 // Serializing an object key caused an encoding change.
3070 FastJsonStringifierObjectKeyResult key_result = SerializeObjectKey<false>(
3071 cont.object_key(), cont.object_key_comma(), no_gc);
3072 USE(key_result);
3074 // Resuming due to encoding change of an object key guarantees that there
3075 // are at least two other objects on the stack (the value for that key, and
3076 // the continuation record for the object the key is a member of).
3077 DCHECK_GE(stack_.size(), 2);
3078 cont = stack_.back();
3079 stack_.pop_back();
3080 // Check that we have the object's continuation record.
3081 DCHECK(ContinuationRecord::IsObjectResumeType(stack_.back().type()));
3082 }
3084 // TrySerializeSimpleObject won't trigger GCs. See DisableGCMole scopes in
3085 // SerializeJSPrimitiveWrapper for explanation.
3086 DisableGCMole no_gc_mole;
3088 TrySerializeSimpleObject(cont.simple_object());
3089 if (V8_UNLIKELY(result != SUCCESS)) {
3091 return result;
3092 }
3093
3094 // Early return if a top-level string triggered the encoding change.
3095 if (stack_.empty()) return result;
3096
3097 cont = stack_.back();
3098 stack_.pop_back();
3099 }
3102 return SerializeObject(cont, no_gc);
3103}
3104
3105template <typename Char>
3107 Tagged<JSAny> object, const DisallowGarbageCollection& no_gc) {
3108 // Initial checks if using the fast-path is possible in general.
3109 if (!object.IsSmi() && (IsJSObject(object) || IsJSArray(object))) {
3111 // Load initial JSObject/JSArray prototypes from native context.
3112 Tagged<Map> meta_map = obj->map()->map();
3113 // Don't deal with context-less meta maps.
3114 if (V8_UNLIKELY(meta_map == ReadOnlyRoots(isolate_).meta_map())) {
3115 return SLOW_PATH;
3116 }
3117 Tagged<NativeContext> native_context = meta_map->native_context();
3118 initial_jsobject_proto_ = native_context->initial_object_prototype();
3119 initial_jsarray_proto_ = native_context->initial_array_prototype();
3120 // Prototypes don't have interesting properties (toJSON).
3121 if (V8_UNLIKELY(
3122 initial_jsarray_proto_->map()->may_have_interesting_properties())) {
3123 return SLOW_PATH;
3124 }
3125 if (V8_UNLIKELY(initial_jsobject_proto_->map()
3126 ->may_have_interesting_properties())) {
3127 return SLOW_PATH;
3128 }
3129 // JSArray's prototype is the initial object prototype.
3130 Tagged<HeapObject> jsarray_proto_proto =
3131 initial_jsarray_proto_->map()->prototype();
3132 if (V8_UNLIKELY(jsarray_proto_proto != initial_jsobject_proto_)) {
3133 return SLOW_PATH;
3134 }
3135 }
3136
3137 // TrySerializeSimpleObject won't trigger GCs. See DisableGCMole scopes in
3138 // SerializeJSPrimitiveWrapper for explanation.
3139 DisableGCMole no_gc_mole;
3140 // Serialize object.
3141 FastJsonStringifierResult result = TrySerializeSimpleObject(object);
3142 if constexpr (is_one_byte) {
3144 DCHECK(IsString(object) || IsStringWrapper(object));
3145 stack_.push_back(ContinuationRecord::ForSimpleObject(object));
3146 return result;
3147 }
3148 } else {
3150 }
3151 if (result != JS_OBJECT && result != JS_ARRAY) {
3152 return result;
3153 }
3154 return SerializeObject(ContinuationRecord::ForJSAny(object, result), no_gc);
3155}
3156
3157template <typename Char>
3159 ContinuationRecord cont, const DisallowGarbageCollection& no_gc) {
3160 // GCMole doesn't handle kNoGC interrupts correctly.
3161 DisableGCMole no_gc_mole;
3162
3163 uint32_t interrupt_budget = kGlobalInterruptBudget;
3164
3166 while (true) {
3167 --interrupt_budget;
3168 if (V8_UNLIKELY(interrupt_budget == 0)) {
3169 result = HandleInterruptAndCheckCycle();
3170 if (V8_UNLIKELY(result != SUCCESS)) return result;
3171 interrupt_budget = kGlobalInterruptBudget;
3172 }
3173 switch (cont.type()) {
3175 result = SerializeJSObject(cont.js_object(), no_gc);
3176 break;
3177 }
3179 result = ResumeJSObject<ResumeJSObjectMode::kBuildingMapCache>(
3180 cont.js_object(), cont.object_map(), cont.object_descriptor_idx(),
3183 true, no_gc);
3184 break;
3185 }
3187 result = ResumeJSObject<ResumeJSObjectMode::kWithMapCache>(
3188 cont.js_object(), cont.object_map(), cont.object_descriptor_idx(),
3191 true, no_gc);
3192 break;
3193 }
3195 result = ResumeJSObject<ResumeJSObjectMode::kWithoutMapCache>(
3196 cont.js_object(), cont.object_map(), cont.object_descriptor_idx(),
3199 true, no_gc);
3200 break;
3201 }
3203 result = SerializeJSArray(cont.js_array());
3204 break;
3205 }
3207 result = SerializeFixedArray<PACKED_ELEMENTS>(
3208 cont.array_elements(), cont.array_index(), cont.array_length());
3209 break;
3210 }
3212 result = SerializeFixedArray<HOLEY_ELEMENTS>(
3213 cont.array_elements(), cont.array_index(), cont.array_length());
3214 break;
3215 }
3217 result = SerializeFixedArrayWithInterruptCheck<PACKED_ELEMENTS>(
3218 cont.array_elements(), cont.array_index(), cont.array_length());
3219 break;
3220 }
3222 result = SerializeFixedArrayWithInterruptCheck<HOLEY_ELEMENTS>(
3223 cont.array_elements(), cont.array_index(), cont.array_length());
3224 break;
3225 }
3226 default:
3227 UNREACHABLE();
3228 }
3229 static_assert(SUCCESS == 0);
3230 static_assert(JS_OBJECT == 1);
3231 static_assert(JS_ARRAY == 2);
3232 if (V8_UNLIKELY(result > JS_ARRAY)) return result;
3233 if (stack_.empty()) return SUCCESS;
3234 cont = stack_.back();
3235 stack_.pop_back();
3236 }
3237}
3238
3239template <typename Char>
3242 StackLimitCheck interrupt_check(isolate_);
3243 {
3244 // A TerminationException can actually trigger a GC when allocating the
3245 // message object. We don't touch any of the heap objects after we
3246 // encountered an exception, so this is fine.
3247 AllowGarbageCollection allow_gc;
3248 if (V8_UNLIKELY(interrupt_check.InterruptRequested() &&
3249 IsException(isolate_->stack_guard()->HandleInterrupts(
3251 isolate_))) {
3252 return EXCEPTION;
3253 }
3254 }
3255
3256 if (V8_UNLIKELY(CheckCycle())) {
3257 // TODO(pthier): Construct exception message on fast-path and avoid falling
3258 // back to slow path just to handle the exception.
3259 return SLOW_PATH;
3260 }
3261
3262 return SUCCESS;
3263}
3264
3265template <typename Char>
3267 std::unordered_set<Address> set;
3268 for (uint32_t i = 0; i < stack_.size(); i++) {
3269 ContinuationRecord rec = stack_[i];
3270 if (rec.type() == ContinuationRecord::kObjectKey ||
3272 continue;
3273 Tagged<Object> obj = rec.object();
3274 if (V8_UNLIKELY(set.find(obj.ptr()) != set.end())) {
3275 return true;
3276 }
3277 set.insert(obj.ptr());
3278 }
3279 return false;
3280}
3281
3282template <typename Char>
3283template <typename SrcChar>
3284 requires(sizeof(SrcChar) == sizeof(uint8_t))
3286 const SrcChar* chars, size_t length,
3287 const DisallowGarbageCollection& no_gc) {
3288 constexpr int kUseSimdLengthThreshold = 32;
3289 if (length >= kUseSimdLengthThreshold) {
3290 return AppendStringSIMD(chars, length, no_gc);
3291 }
3292 return AppendStringSWAR(chars, length, 0, 0, no_gc);
3293}
3294
3295template <typename Char>
3296template <typename SrcChar>
3298 const SrcChar* chars, size_t length,
3299 const DisallowGarbageCollection& no_gc) {
3300 buffer_.Append(chars, length);
3301}
3302
3303template <typename Char>
3304template <typename SrcChar>
3305 requires(sizeof(SrcChar) == sizeof(uint8_t))
3307 const SrcChar* chars, size_t length, size_t start,
3308 size_t uncopied_src_index, const DisallowGarbageCollection& no_gc) {
3309 bool needs_escaping = false;
3310 for (size_t i = start; i < length; i++) {
3311 SrcChar c = chars[i];
3312 if (V8_LIKELY(DoNotEscape(c))) continue;
3313 needs_escaping = true;
3314 buffer_.Append(chars + uncopied_src_index, i - uncopied_src_index);
3315 AppendCStringUnchecked(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
3316 uncopied_src_index = i + 1;
3317 }
3318 if (V8_LIKELY(uncopied_src_index < length)) {
3319 buffer_.Append(chars + uncopied_src_index, length - uncopied_src_index);
3320 }
3321 return needs_escaping;
3322}
3323
3324template <typename Char>
3325template <typename SrcChar>
3326 requires(sizeof(SrcChar) == sizeof(uint8_t))
3327V8_CLANG_NO_SANITIZE("alignment")
3329 const SrcChar* chars, size_t length, size_t start,
3330 size_t uncopied_src_index, const DisallowGarbageCollection& no_gc) {
3331 using PackedT = uint32_t;
3332 static constexpr size_t stride = sizeof(PackedT);
3333 size_t i = start;
3334 for (; i + (stride - 1) < length; i += stride) {
3335 PackedT packed = *reinterpret_cast<const PackedT*>(chars + i);
3336 if (V8_UNLIKELY(NeedsEscape(packed))) break;
3337 }
3338 return AppendStringScalar(chars, length, i, uncopied_src_index, no_gc);
3339}
3340
3341template <typename Char>
3342template <typename SrcChar>
3343 requires(sizeof(SrcChar) == sizeof(uint8_t))
3345 const SrcChar* chars, size_t length,
3346 const DisallowGarbageCollection& no_gc) {
3347 namespace hw = hwy::HWY_NAMESPACE;
3348
3349 bool needs_escaping = false;
3350 size_t uncopied_src_index = 0; // Index of first char not copied yet.
3351 const SrcChar* block = chars;
3352 const SrcChar* end = chars + length;
3353 hw::FixedTag<SrcChar, 16> tag;
3354 static constexpr size_t stride = hw::Lanes(tag);
3355
3356 const auto mask_0x20 = hw::Set(tag, 0x20);
3357 const auto mask_0x22 = hw::Set(tag, 0x22);
3358 const auto mask_0x5c = hw::Set(tag, 0x5c);
3359
3360 for (; block + (stride - 1) < end; block += stride) {
3361 const auto input = hw::LoadU(tag, block);
3362 const auto has_lower_than_0x20 = input < mask_0x20;
3363 const auto has_0x22 = input == mask_0x22;
3364 const auto has_0x5c = input == mask_0x5c;
3365 const auto result = hw::Or(hw::Or(has_lower_than_0x20, has_0x22), has_0x5c);
3366
3367 // No character that needs escaping found in block.
3368 if (V8_LIKELY(hw::AllFalse(tag, result))) continue;
3369
3370 needs_escaping = true;
3371 size_t index = hw::FindKnownFirstTrue(tag, result);
3372 Char found_char = block[index];
3373 const size_t char_index = block - chars + index;
3374 const size_t copy_length = char_index - uncopied_src_index;
3375 buffer_.Append(chars + uncopied_src_index, copy_length);
3376 AppendCStringUnchecked(
3377 &JsonEscapeTable[found_char * kJsonEscapeTableEntrySize]);
3378 uncopied_src_index = char_index + 1;
3379 // Advance to character after the one that was found to need escaping.
3380 block += index + 1;
3381 // Subtract stride as it will be added again at the beginning of the loop.
3382 block -= stride;
3383 }
3384
3385 // Handle remaining characters.
3386 const size_t start_index = block - chars;
3387 return AppendStringSWAR(chars, length, start_index, uncopied_src_index,
3388 no_gc) ||
3389 needs_escaping;
3390}
3391
3392template <typename Char>
3393template <typename SrcChar>
3394 requires(sizeof(SrcChar) == sizeof(base::uc16))
3396 const SrcChar* chars, size_t length,
3397 const DisallowGarbageCollection& no_gc) {
3398 bool needs_escaping = false;
3399 uint32_t uncopied_src_index = 0; // Index of first char not copied yet.
3400 // TODO(pthier): Add SIMD version.
3401 for (uint32_t i = 0; i < length; i++) {
3402 SrcChar c = chars[i];
3403 if (V8_LIKELY(DoNotEscape(c))) continue;
3404 needs_escaping = true;
3405 if (sizeof(SrcChar) != 1 && base::IsInRange(c, static_cast<SrcChar>(0xD800),
3406 static_cast<SrcChar>(0xDFFF))) {
3407 // The current character is a surrogate.
3408 buffer_.Append(chars + uncopied_src_index, i - uncopied_src_index);
3409 char double_to_radix_chars[kDoubleToRadixMaxChars];
3410 base::Vector<char> double_to_radix_buffer =
3411 base::ArrayVector(double_to_radix_chars);
3412 if (c <= 0xDBFF) {
3413 // The current character is a leading surrogate.
3414 if (i + 1 < length) {
3415 // There is a next character.
3416 SrcChar next = chars[i + 1];
3417 if (base::IsInRange(next, static_cast<SrcChar>(0xDC00),
3418 static_cast<SrcChar>(0xDFFF))) {
3419 // The next character is a trailing surrogate, meaning this is a
3420 // surrogate pair.
3421 AppendCharacterUnchecked(c);
3422 AppendCharacterUnchecked(next);
3423 i++;
3424 } else {
3425 // The next character is not a trailing surrogate. Thus, the
3426 // current character is a lone leading surrogate.
3427 AppendCStringLiteralUnchecked("\\u");
3428 std::string_view hex =
3429 DoubleToRadixStringView(c, 16, double_to_radix_buffer);
3430 AppendStringUnchecked(hex);
3431 }
3432 } else {
3433 // There is no next character. Thus, the current character is a lone
3434 // leading surrogate.
3435 AppendCStringLiteralUnchecked("\\u");
3436 std::string_view hex =
3437 DoubleToRadixStringView(c, 16, double_to_radix_buffer);
3438 AppendStringUnchecked(hex);
3439 }
3440 } else {
3441 // The current character is a lone trailing surrogate. (If it had been
3442 // preceded by a leading surrogate, we would've ended up in the other
3443 // branch earlier on, and the current character would've been handled
3444 // as part of the surrogate pair already.)
3445 AppendCStringLiteralUnchecked("\\u");
3446 std::string_view hex =
3447 DoubleToRadixStringView(c, 16, double_to_radix_buffer);
3448 AppendStringUnchecked(hex);
3449 }
3450 uncopied_src_index = i + 1;
3451 } else {
3452 buffer_.Append(chars + uncopied_src_index, i - uncopied_src_index);
3453 DCHECK_LT(c, 0x60);
3454 AppendCStringUnchecked(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
3455 uncopied_src_index = i + 1;
3456 }
3457 }
3458 if (uncopied_src_index < length) {
3459 buffer_.Append(chars + uncopied_src_index, length - uncopied_src_index);
3460 }
3461 return needs_escaping;
3462}
3463
3464namespace {
3465
3466bool CanUseFastStringifier(DirectHandle<JSAny> replacer,
3467 DirectHandle<Object> gap) {
3468 // TODO(pthier): Support gap on fast-path.
3469 return v8_flags.json_stringify_fast_path && IsUndefined(*replacer) &&
3470 IsUndefined(*gap);
3471}
3472
3473MaybeDirectHandle<Object> FastJsonStringify(Isolate* isolate,
3474 Handle<JSAny> object) {
3476
3477 FastJsonStringifier<uint8_t> one_byte_stringifier(isolate);
3478 std::optional<FastJsonStringifier<base::uc16>> two_byte_stringifier;
3480 one_byte_stringifier.SerializeObject(*object, no_gc);
3481 bool result_is_one_byte = true;
3482
3483 if (result == CHANGE_ENCODING) {
3484 two_byte_stringifier.emplace(isolate);
3485 result = two_byte_stringifier->ResumeFrom(one_byte_stringifier, no_gc);
3486 DCHECK_NE(result, CHANGE_ENCODING);
3487 result_is_one_byte = false;
3488 }
3489
3490 if (V8_LIKELY(result == SUCCESS)) {
3491 if (result_is_one_byte) {
3492 const size_t length = one_byte_stringifier.ResultLength();
3493 DirectHandle<SeqOneByteString> ret;
3494 {
3495 AllowGarbageCollection allow_gc;
3496 if (length > String::kMaxLength) {
3497 THROW_NEW_ERROR(isolate, NewInvalidStringLengthError());
3498 }
3500 isolate, ret,
3501 isolate->factory()->NewRawOneByteString(static_cast<int>(length)));
3502 }
3503 one_byte_stringifier.CopyResultTo(ret->GetChars(no_gc));
3504 return ret;
3505 } else {
3506 DCHECK(two_byte_stringifier.has_value());
3507 const size_t one_byte_length = one_byte_stringifier.ResultLength();
3508 const size_t two_byte_length = two_byte_stringifier->ResultLength();
3509 const size_t total_length = one_byte_length + two_byte_length;
3510 DirectHandle<SeqTwoByteString> ret;
3511 {
3512 AllowGarbageCollection allow_gc;
3513 if (total_length > String::kMaxLength) {
3514 THROW_NEW_ERROR(isolate, NewInvalidStringLengthError());
3515 }
3516 ASSIGN_RETURN_ON_EXCEPTION(isolate, ret,
3517 isolate->factory()->NewRawTwoByteString(
3518 static_cast<int>(total_length)));
3519 }
3520 base::uc16* chars = ret->GetChars(no_gc);
3521 if (one_byte_length > 0) {
3522 one_byte_stringifier.CopyResultTo(chars);
3523 }
3524 DCHECK_GT(two_byte_length, 0);
3525 two_byte_stringifier->CopyResultTo(chars + one_byte_length);
3526 return ret;
3527 }
3528 } else if (result == UNDEFINED) {
3529 return isolate->factory()->undefined_value();
3530 } else if (result == SLOW_PATH) {
3531 // TODO(pthier): Resume instead of restarting.
3532 AllowGarbageCollection allow_gc;
3533 JsonStringifier stringifier(isolate);
3534 Handle<JSAny> undefined = isolate->factory()->undefined_value();
3535 return stringifier.Stringify(object, undefined, undefined);
3536 }
3537 DCHECK(result == EXCEPTION);
3538 CHECK(isolate->has_exception());
3539 return MaybeDirectHandle<Object>();
3540}
3541
3542} // namespace
3543
3545 Handle<JSAny> replacer,
3546 Handle<Object> gap) {
3547 if (CanUseFastStringifier(replacer, gap)) {
3548 return FastJsonStringify(isolate, object);
3549 } else {
3550 JsonStringifier stringifier(isolate);
3551 return stringifier.Stringify(object, replacer, gap);
3552 }
3553}
3554
3555} // namespace internal
3556} // namespace v8
Isolate * isolate_
Builtins::Kind kind
Definition builtins.cc:40
V8_INLINE T FromJust() const &
Definition v8-maybe.h:64
V8_INLINE bool IsNothing() const
Definition v8-maybe.h:35
bool IsArray() const
Definition api.cc:3525
constexpr size_t size() const
Definition vector.h:70
constexpr T * begin() const
Definition vector.h:96
constexpr T * end() const
Definition vector.h:103
void AppendNormalLine(DirectHandle< Object > key, DirectHandle< Object > object)
void AppendClosingLine(DirectHandle< Object > closing_key)
void AppendStartLine(DirectHandle< Object > start_object)
void AppendKey(DirectHandle< Object > key)
void AppendConstructorName(DirectHandle< Object > object)
Tagged< DescriptorArray > object_descriptors() const
static constexpr ContinuationRecord ForJSArrayResume(Tagged< FixedArrayBase > obj, uint32_t index, uint32_t length)
static constexpr bool IsObjectResumeType(Type type)
static constexpr Type ContinuationTypeFromResult(FastJsonStringifierResult result)
constexpr ContinuationRecord(Type type, Tagged< ObjectT > obj, uint32_t index, uint32_t length)
Tagged< JSArray > js_array() const
static constexpr ContinuationRecord ForJSObjectResume(Tagged< JSAny > obj, Tagged< Map > map, uint16_t descriptor_idx, uint16_t nof_descriptors, uint8_t in_object_properties, uint8_t in_object_properties_start, Tagged< DescriptorArray > descriptors)
struct v8::internal::ContinuationRecord::@88::@92 object_key_
static constexpr ContinuationRecord ForObjectKey(Tagged< String > key, bool comma)
Tagged< DescriptorArray > descriptors
static constexpr ContinuationRecord ForJSArray(Tagged< JSAny > obj)
static constexpr ContinuationRecord ForSimpleObject(Tagged< JSAny > obj)
static constexpr ContinuationRecord ForJSObject(Tagged< JSAny > obj)
Tagged< JSAny > simple_object() const
struct v8::internal::ContinuationRecord::@88::@91 js_object_
UnionOf< JSAny, FixedArrayBase > ObjectT
Tagged< ObjectT > object() const
static consteval Type ContinuationTypeForArray(ElementsKind kind, bool with_interrupt_check)
Tagged< FixedArrayBase > array_elements() const
Tagged< JSObject > js_object() const
struct v8::internal::ContinuationRecord::@88::@90 js_array_
static constexpr ContinuationRecord ForJSAny(Tagged< JSAny > obj, FastJsonStringifierResult result)
constexpr ContinuationRecord(Type type, Tagged< ObjectT > obj, Tagged< Map > map, uint16_t descriptor_idx, uint16_t nof_descriptors, uint8_t in_object_properties, uint8_t in_object_properties_start, Tagged< DescriptorArray > descriptors)
static constexpr bool IsArrayResumeType(Type type)
Tagged< String > object_key() const
constexpr ContinuationRecord(Type type, Tagged< ObjectT > obj, bool comma)
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT MaybeHandle< Object > Call(Isolate *isolate, DirectHandle< Object > callable, DirectHandle< Object > receiver, base::Vector< const DirectHandle< Object > > args)
Definition execution.cc:523
MaybeHandle< String > NewStringFromOneByte(base::Vector< const uint8_t > string, AllocationType allocation=AllocationType::kYoung)
V8_WARN_UNUSED_RESULT Handle< String > NumberToString(DirectHandle< Object > number, NumberCacheMode mode=NumberCacheMode::kBoth)
Handle< JSObject > NewJSObject(DirectHandle< JSFunction > constructor, AllocationType allocation=AllocationType::kYoung, NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:2985
DirectHandle< Object > NewInvalidStringLengthError()
Definition factory.cc:2820
Handle< OrderedHashSet > NewOrderedHashSet()
Definition factory.cc:556
V8_WARN_UNUSED_RESULT MaybeHandle< String > NewStringFromTwoByte(base::Vector< const base::uc16 > str, AllocationType allocation=AllocationType::kYoung)
Definition factory.cc:906
Handle< String > InternalizeString(base::Vector< const char > str, bool convert_encoding=false)
Definition factory.h:216
V8_INLINE void AppendStringUnchecked(std::string_view str)
void CopyResultTo(DstChar *out_buffer)
V8_INLINE void AppendCharacterUnchecked(SrcChar c)
V8_NOINLINE FastJsonStringifierResult ResumeFrom(FastJsonStringifier< OldChar > &old_stringifier, const DisallowGarbageCollection &no_gc)
V8_INLINE bool AppendString(const SrcChar *chars, size_t length, const DisallowGarbageCollection &no_gc)
FastJsonStringifierResult SerializeJSArray(Tagged< JSArray > array)
V8_INLINE void Separator(bool comma)
static constexpr uint32_t kGlobalInterruptBudget
FastJsonStringifierResult TrySerializeSimpleObject(Tagged< JSAny > object)
V8_NOINLINE FastJsonStringifierResult SerializeJSPrimitiveWrapper(Tagged< JSPrimitiveWrapper > obj, const DisallowGarbageCollection &no_gc)
V8_INLINE FastJsonStringifierResult SerializeString(Tagged< HeapObject > str, const DisallowGarbageCollection &no_gc)
V8_INLINE FastJsonStringifierResult SerializeObject(Tagged< JSAny > object, const DisallowGarbageCollection &no_gc)
V8_INLINE bool AppendString(const SrcChar *chars, size_t length, const DisallowGarbageCollection &no_gc)
V8_INLINE void AppendCStringUnchecked(const char *chars)
V8_INLINE FastJsonStringifierResult SerializeFixedArray(Tagged< FixedArrayBase > array, uint32_t start_idx, uint32_t length)
V8_INLINE void AppendCStringUnchecked(const char *chars, size_t len)
V8_INLINE bool AppendStringSWAR(const SrcChar *chars, size_t length, size_t start, size_t uncopied_src_index, const DisallowGarbageCollection &no_gc)
V8_INLINE void SeparatorUnchecked(bool comma)
V8_INLINE void AppendCStringLiteralUnchecked(const char(&literal)[N])
Tagged< HeapObject > initial_jsarray_proto_
FastJsonStringifierObjectKeyResult SerializeObjectKey(Tagged< String > key, bool comma, const DisallowGarbageCollection &no_gc)
V8_INLINE void AppendStringNoEscapes(const SrcChar *chars, size_t length, const DisallowGarbageCollection &no_gc)
V8_INLINE FastJsonStringifierResult SerializeJSObject(Tagged< JSObject > obj, const DisallowGarbageCollection &no_gc)
bool AppendStringScalar(const SrcChar *chars, size_t length, size_t start, size_t uncopied_src_index, const DisallowGarbageCollection &no_gc)
V8_NOINLINE FastJsonStringifierResult HandleInterruptAndCheckCycle()
V8_INLINE void AppendCString(const char *chars, size_t len)
V8_INLINE void AppendCStringLiteral(const char(&literal)[N])
ZoneUnorderedMap< Tagged< Map >, bool, Object::Hasher > map_cache_
FastJsonStringifierResult SerializeFixedArrayWithInterruptCheck(Tagged< FixedArrayBase > elements, uint32_t start_index, uint32_t length)
V8_INLINE bool AppendStringSIMD(const SrcChar *chars, size_t length, const DisallowGarbageCollection &no_gc)
V8_INLINE FastJsonStringifierResult ResumeJSObject(Tagged< JSObject > obj, Tagged< Map > map, uint16_t start_descriptor_idx, uint16_t nof_descriptors, uint8_t in_object_properties, uint8_t in_object_properties_start, Tagged< DescriptorArray > descriptors, bool comma, const DisallowGarbageCollection &no_gc)
base::SmallVector< ContinuationRecord, 16 > stack_
V8_INLINE void SerializeSmi(Tagged< Smi > object)
V8_INLINE FastJsonStringifierResult SerializeFixedArrayElement(Tagged< T > elements, uint32_t i, uint32_t length)
V8_INLINE void AppendString(std::string_view str)
V8_INLINE void AppendCharacter(SrcChar c)
V8_INLINE void EnsureCapacity(size_t size)
static constexpr uint32_t kArrayInterruptLength
V8_INLINE void AppendCString(const char *chars)
Tagged< HeapObject > initial_jsobject_proto_
static FieldIndex ForDetails(Tagged< Map > map, PropertyDetails details)
static constexpr int kMaxLength
V8_INLINE bool is_null() const
Definition handles.h:69
HandleType< T > CloseAndEscape(HandleType< T > handle_value)
V8_INLINE void AppendCString(const SrcChar *s)
MaybeDirectHandle< String > Finish()
V8_INLINE void AppendCharacter(uint8_t c)
V8_INLINE void AppendString(std::string_view str)
V8_INLINE void AppendCStringLiteral(const char(&literal)[N])
Tagged< Object > Throw(Tagged< Object > exception, MessageLocation *location=nullptr)
Definition isolate.cc:2091
StackGuard * stack_guard()
Definition isolate.h:1198
v8::internal::Factory * factory()
Definition isolate.h:1527
LocalHeap * main_thread_local_heap()
Definition isolate.cc:7479
Tagged< Object > StackOverflow()
Definition isolate.cc:1862
static V8_EXPORT_PRIVATE void AddProperty(Isolate *isolate, DirectHandle< JSObject > object, DirectHandle< Name > name, DirectHandle< Object > value, PropertyAttributes attributes)
static Handle< JSAny > FastPropertyAt(Isolate *isolate, DirectHandle< JSObject > object, Representation representation, FieldIndex index)
static const int kRawJsonInitialIndex
Definition js-raw-json.h:29
static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetElement(Isolate *isolate, DirectHandle< JSReceiver > receiver, uint32_t index)
static DirectHandle< String > GetConstructorName(Isolate *isolate, DirectHandle< JSReceiver > receiver)
V8_INLINE void AppendSubstring(const SrcChar *src, size_t from, size_t to)
NoExtendBuilder(DestChar *start, size_t *current_index)
V8_INLINE void AppendChars(base::Vector< const uint8_t > chars, size_t length)
V8_INLINE void AppendString(std::string_view str)
V8_INLINE void Separator(bool first)
V8_INLINE void AppendCharacter(uint8_t c)
uint8_t one_byte_array_[kInitialPartLength]
bool InitializeGap(Handle< Object > gap)
V8_INLINE Result SerializeJSArray(Handle< JSArray > object, Handle< Object > key)
V8_INLINE Result SerializeFixedArrayWithInterruptCheck(DirectHandle< JSArray > array, uint32_t length, uint32_t *slow_path_index)
V8_WARN_UNUSED_RESULT MaybeDirectHandle< Object > Stringify(Handle< JSAny > object, Handle< JSAny > replacer, Handle< Object > gap)
Result SerializeJSPrimitiveWrapper(Handle< JSPrimitiveWrapper > object, Handle< Object > key)
static constexpr int kCircularErrorMessagePostfixCount
bool SerializeString(Handle< String > object)
Result Serialize_(Handle< JSAny > object, bool comma, Handle< Object > key)
DirectHandle< JSReceiver > CurrentHolder(DirectHandle< Object > value, DirectHandle< Object > inital_holder)
static constexpr int kCircularErrorMessagePrefixCount
static V8_INLINE bool SerializeStringUnchecked_(base::Vector< const SrcChar > src, NoExtendBuilder< DestChar > *dest)
V8_INLINE Result SerializeProperty(Handle< JSAny > object, bool deferred_comma, Handle< String > deferred_key)
static constexpr size_t kMaxPartLength
V8_INLINE Result SerializeHeapNumber(DirectHandle< HeapNumber > object)
static constexpr size_t kPartLengthGrowthFactor
V8_INLINE void AppendCString(const SrcChar *s)
V8_INLINE Result SerializeJSObject(Handle< JSObject > object, Handle< Object > key)
V8_INLINE Result SerializeObject(Handle< JSAny > obj)
void AppendStringByCopy(Tagged< String > string, size_t length, const DisallowGarbageCollection &no_gc)
bool InitializeReplacer(Handle< JSAny > replacer)
Handle< String > ConstructCircularStructureErrorMessage(DirectHandle< Object > last_key, size_t start_index)
V8_INLINE bool SerializeString_(Tagged< String > string, const DisallowGarbageCollection &no_gc)
Result SerializeSmi(Tagged< Smi > object)
Result SerializeArrayLikeSlow(DirectHandle< JSReceiver > object, uint32_t start, uint32_t length)
Handle< FixedArray > property_list_
V8_INLINE bool CurrentPartCanFit(size_t length)
V8_INLINE void AppendCStringLiteral(const char(&literal)[N])
V8_WARN_UNUSED_RESULT MaybeHandle< JSAny > ApplyToJsonFunction(Handle< JSAny > object, DirectHandle< Object > key)
SimplePropertyKeyCache key_cache_
V8_INLINE void Append(SrcChar c)
static constexpr size_t kInitialPartLength
Result SerializeDouble(double number)
void AppendSubstringByCopy(const SrcChar *src, size_t count)
Result SerializeJSProxy(Handle< JSProxy > object, Handle< Object > key)
V8_INLINE Result SerializeElement(Isolate *isolate, Handle< JSAny > object, int i)
V8_INLINE void AppendString(std::basic_string_view< SrcChar > s)
V8_INLINE Result SerializeFixedArrayElement(Tagged< T > elements, uint32_t i, Tagged< JSArray > array, bool can_treat_hole_as_undefined)
V8_WARN_UNUSED_RESULT MaybeHandle< JSAny > ApplyReplacerFunction(Handle< JSAny > value, DirectHandle< Object > key, DirectHandle< Object > initial_holder)
V8_INLINE bool EscapedLengthIfCurrentPartFits(size_t length)
V8_NOINLINE void AppendSubstring(const SrcChar *src, size_t from, size_t to)
V8_INLINE Result SerializeFixedArrayWithPossibleTransitions(DirectHandle< JSArray > array, uint32_t length, uint32_t *slow_path_index)
Result SerializeJSReceiverSlow(DirectHandle< JSReceiver > object)
Handle< JSReceiver > replacer_function_
V8_INLINE void SerializeDeferredKey(bool deferred_comma, Handle< Object > deferred_key)
std::pair< Handle< Object >, Handle< Object > > KeyObject
std::vector< KeyObject > stack_
Result StackPush(Handle< Object > object, Handle< Object > key)
V8_NOINLINE void AppendString(Handle< String > string_handle)
bool TrySerializeSimplePropertyKey(Tagged< String > string, const DisallowGarbageCollection &no_gc)
static MaybeHandle< FixedArray > GetKeys(Isolate *isolate, DirectHandle< JSReceiver > object, KeyCollectionMode mode, PropertyFilter filter, GetKeysConversion keys_conversion=GetKeysConversion::kKeepNumbers, bool is_for_in=false, bool skip_indices=false)
Definition keys.cc:97
void RemoveGCEpilogueCallback(GCEpilogueCallback *callback, void *data)
V8_WARN_UNUSED_RESULT V8_INLINE bool ToHandle(Handle< S > *out) const
static bool ToArrayLength(Tagged< Object > obj, uint32_t *index)
static V8_WARN_UNUSED_RESULT HandleType< String >::MaybeType ToString(Isolate *isolate, HandleType< T > input)
static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetPropertyOrElement(Isolate *isolate, DirectHandle< JSAny > object, DirectHandle< Name > name)
static V8_WARN_UNUSED_RESULT HandleType< Number >::MaybeType ToNumber(Isolate *isolate, HandleType< T > input)
static double NumberValue(Tagged< Number > obj)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< Object > GetLengthFromArrayLike(Isolate *isolate, DirectHandle< JSReceiver > object)
Definition objects.cc:1238
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetProperty(LookupIterator *it, bool is_global_reference=false)
Definition objects.cc:1248
static bool ToUint32(Tagged< Object > obj, uint32_t *value)
static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetElement(Isolate *isolate, DirectHandle< JSAny > object, uint32_t index)
static constexpr uint8_t kNull
Definition oddball.h:56
static constexpr uint8_t kFalse
Definition oddball.h:53
static constexpr uint8_t kTrue
Definition oddball.h:54
static Handle< FixedArray > ConvertToKeysArray(Isolate *isolate, Handle< OrderedHashSet > table, GetKeysConversion convert)
static HandleType< OrderedHashSet >::MaybeType Add(Isolate *isolate, HandleType< OrderedHashSet > table, DirectHandle< Object > value)
V8_INLINE size_t SegmentFreeChars() const
V8_NOINLINE V8_PRESERVE_MOST void Extend(size_t min_size)
static constexpr size_t kStackBufferSize
static constexpr uint8_t kNumVariableSegments
V8_INLINE void AppendCharacter(SrcChar c)
V8_INLINE size_t StackBufferLength() const
std::optional< Zone > zone_
void Append(const SrcChar *chars, size_t length)
static constexpr uint32_t kInitialSegmentSize
V8_INLINE size_t SegmentCapacity(size_t segment)
static constexpr uint32_t kMaxSegmentSize
V8_INLINE size_t CurSegmentCapacity()
static constexpr uint8_t kInitialSegmentSizeHighestBit
AccountingAllocator * allocator_
V8_INLINE size_t CurSegmentLength() const
static constexpr uint8_t kMaxSegmentSizeHighestBit
OutBuffer(AccountingAllocator *allocator)
std::optional< ZoneList< base::Vector< Char > > > segments_
V8_INLINE bool ZoneUsed() const
Char stack_buffer_[kStackBufferSize]
void EnsureCapacity(size_t size)
V8_INLINE void ReduceCurrentCapacity(size_t size)
PropertyLocation location() const
Representation representation() const
static constexpr PropertyDetails Empty(PropertyCellType cell_type=PropertyCellType::kNoCell)
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
static constexpr int kMinValue
Definition smi.h:100
static constexpr Tagged< Smi > zero()
Definition smi.h:99
static constexpr int kMaxValue
Definition smi.h:101
Tagged< Object > HandleInterrupts(InterruptLevel level=InterruptLevel::kAnyEffect)
V8_INLINE bool InterruptRequested()
Definition isolate.h:3051
static void WriteToFlat(Tagged< String > source, SinkCharT *sink, uint32_t start, uint32_t length)
Definition string.cc:772
static const uint32_t kMaxLength
Definition string.h:511
static V8_INLINE HandleType< String > Flatten(Isolate *isolate, HandleType< T > string, AllocationType allocation=AllocationType::kYoung)
static const int32_t kMaxOneByteCharCode
Definition string.h:500
static bool IsOneByteRepresentationUnderneath(Tagged< String > string)
Definition string-inl.h:373
static PtrType Relaxed_Load(Tagged< HeapObject > host, int offset=0)
V8_INLINE constexpr StorageType ptr() const
static V8_INLINE Tagged_t CompressObject(Address tagged)
Zone * zone_
base::OwnedVector< uint8_t > buffer_
Definition assembler.cc:111
#define COMPRESS_POINTERS_BOOL
Definition globals.h:99
int start
uint32_t count
int end
#define ASSIGN_RETURN_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:291
#define THROW_NEW_ERROR(isolate, call)
Definition isolate.h:307
#define ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)
Definition isolate.h:276
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
int32_t offset
std::map< const std::string, const std::string > map
const std::string property
double second
#define CASE_WITH_INTERRUPT(kind)
#define CASE_WITH_TRANSITION(kind)
ZoneVector< RpoNumber > & result
Point from
FunctionLiteral * literal
Definition liveedit.cc:294
int s
Definition mul-fft.cc:297
constexpr bool IsPowerOfTwo(T value)
Definition bits.h:187
constexpr unsigned CountLeadingZeros32(uint32_t value)
Definition bits.h:122
constexpr Vector< T > ArrayVector(T(&arr)[N])
Definition vector.h:354
constexpr bool IsInRange(T value, U lower_limit, U higher_limit)
Definition bounds.h:20
uint16_t uc16
Definition strings.h:18
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
Vector< const uint8_t > OneByteVector(const char *data, size_t length)
Definition vector.h:337
V8_INLINE constexpr bool IsTwoByteString(InstanceType instance_type)
void DeleteArray(T *array)
Definition allocation.h:63
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
uint32_t DoubleToUint32(double x)
constexpr int kTaggedSize
Definition globals.h:542
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
constexpr bool IsHoleyElementsKind(ElementsKind kind)
constexpr intptr_t kObjectAlignment
Definition globals.h:930
bool Is(IndirectHandle< U > value)
Definition handles-inl.h:51
bool IsNumber(Tagged< Object > obj)
bool IsCustomElementsReceiverMap(Tagged< Map > map)
Tagged(T object) -> Tagged< T >
bool MayHaveInterestingProperties(Isolate *isolate, Tagged< JSReceiver > object)
constexpr bool IsSmiElementsKind(ElementsKind kind)
static constexpr char kJsonStringifierZoneName[]
V8_INLINE constexpr bool IsSmi(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:665
V8_INLINE IndirectHandle< T > indirect_handle(DirectHandle< T > handle)
Definition handles.h:757
std::string_view IntToStringView(int n, base::Vector< char > buffer)
kInterpreterTrampolineOffset Tagged< HeapObject >
constexpr int N
constexpr bool IsObjectElementsKind(ElementsKind kind)
void MemsetTagged(Tagged_t *start, Tagged< MaybeObject > value, size_t counter)
Definition slots-inl.h:486
Address Tagged_t
Definition globals.h:547
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
PerThreadAssertScopeDebugOnly< true, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > AllowGarbageCollection
V8_INLINE PtrComprCageBase GetPtrComprCageBase()
constexpr int kDoubleToRadixMaxChars
Definition conversions.h:86
DONT_OVERRIDE DISABLE_ALLOCATION_SITES HOLEY_ELEMENTS
constexpr int kBitsPerInt
Definition globals.h:687
typename detail::FlattenUnionHelper< Union<>, Ts... >::type UnionOf
Definition union.h:123
@ UNCACHED_EXTERNAL_INTERNALIZED_ONE_BYTE_STRING_TYPE
@ UNCACHED_EXTERNAL_TWO_BYTE_STRING_TYPE
@ UNCACHED_EXTERNAL_INTERNALIZED_TWO_BYTE_STRING_TYPE
@ UNCACHED_EXTERNAL_ONE_BYTE_STRING_TYPE
@ EXTERNAL_TWO_BYTE_STRING_TYPE
@ EXTERNAL_INTERNALIZED_TWO_BYTE_STRING_TYPE
@ EXTERNAL_INTERNALIZED_ONE_BYTE_STRING_TYPE
@ EXTERNAL_ONE_BYTE_STRING_TYPE
@ INTERNALIZED_ONE_BYTE_STRING_TYPE
@ INTERNALIZED_TWO_BYTE_STRING_TYPE
refactor address components for immediate indexing make OptimizeMaglevOnNextCall optimize to turbofan instead of maglev filter for tracing turbofan compilation trace turbo cfg trace TurboFan s graph trimmer trace TurboFan s control equivalence trace TurboFan s register allocator trace stack load store counters for optimized code in run fuzzing &&concurrent_recompilation trace_turbo trace_turbo_scheduled trace_turbo_stack_accesses verify TurboFan machine graph of code stubs enable FixedArray bounds checks print TurboFan statistics of wasm compilations maximum cumulative size of bytecode considered for inlining scale factor of bytecode size used to calculate the inlining budget * KB
Definition flags.cc:1366
DONT_OVERRIDE DISABLE_ALLOCATION_SITES DISABLE_ALLOCATION_SITES HOLEY_DOUBLE_ELEMENTS
static const int kMaxNumberOfDescriptors
void CopyChars(DstType *dst, const SrcType *src, size_t count) V8_NONNULL(1
V8_EXPORT_PRIVATE FlagValues v8_flags
return value
Definition map-inl.h:893
MaybeDirectHandle< Object > JsonStringify(Isolate *isolate, Handle< JSAny > object, Handle< JSAny > replacer, Handle< Object > gap)
std::string_view DoubleToStringView(double v, base::Vector< char > buffer)
constexpr bool IsDoubleElementsKind(ElementsKind kind)
std::string_view DoubleToRadixStringView(double value, int radix, base::Vector< char > buffer)
!IsContextMap !IsContextMap Tagged< NativeContext >
Definition map-inl.h:877
constexpr uint32_t kMaxUInt32
Definition globals.h:387
T * NewArray(size_t size)
Definition allocation.h:43
!IsContextMap !IsContextMap native_context
Definition map-inl.h:877
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
base::Vector< const char > contents
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NULL(val)
Definition logging.h:491
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define USE(...)
Definition macros.h:293
constexpr T RoundUp(T x, intptr_t m)
Definition macros.h:387
#define V8_INLINE
Definition v8config.h:500
#define V8_CLANG_NO_SANITIZE(what)
defined(V8_TRIVIAL_ABI)
Definition v8config.h:765
#define V8_LIKELY(condition)
Definition v8config.h:661
#define V8_WARN_UNUSED_RESULT
Definition v8config.h:671
#define V8_UNLIKELY(condition)
Definition v8config.h:660
#define V8_NOINLINE
Definition v8config.h:586
#define V8_INLINE_STATEMENT
Definition v8config.h:510
#define V8_PRESERVE_MOST
Definition v8config.h:598