v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
json-parser.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 <optional>
8
10#include "src/base/strings.h"
13#include "src/common/globals.h"
15#include "src/debug/debug.h"
17#include "src/heap/factory.h"
27#include "src/roots/roots.h"
31
32namespace v8 {
33namespace internal {
34
35namespace {
36
37constexpr JsonToken GetOneCharJsonToken(uint8_t c) {
38 // clang-format off
39 return
40 c == '"' ? JsonToken::STRING :
42 c == '-' ? JsonToken::NUMBER :
43 c == '[' ? JsonToken::LBRACK :
44 c == '{' ? JsonToken::LBRACE :
45 c == ']' ? JsonToken::RBRACK :
46 c == '}' ? JsonToken::RBRACE :
47 c == 't' ? JsonToken::TRUE_LITERAL :
48 c == 'f' ? JsonToken::FALSE_LITERAL :
49 c == 'n' ? JsonToken::NULL_LITERAL :
50 c == ' ' ? JsonToken::WHITESPACE :
51 c == '\t' ? JsonToken::WHITESPACE :
52 c == '\r' ? JsonToken::WHITESPACE :
53 c == '\n' ? JsonToken::WHITESPACE :
54 c == ':' ? JsonToken::COLON :
55 c == ',' ? JsonToken::COMMA :
57 // clang-format on
58}
59
60// Table of one-character tokens, by character (0x00..0xFF only).
61static const constexpr JsonToken one_char_json_tokens[256] = {
62#define CALL_GET_SCAN_FLAGS(N) GetOneCharJsonToken(N),
64#undef CALL_GET_SCAN_FLAGS
65#define CALL_GET_SCAN_FLAGS(N) GetOneCharJsonToken(128 + N),
67#undef CALL_GET_SCAN_FLAGS
68};
69
70enum class EscapeKind : uint8_t {
71 kIllegal,
72 kSelf,
73 kBackspace,
74 kTab,
75 kNewLine,
76 kFormFeed,
77 kCarriageReturn,
78 kUnicode
79};
80
81using EscapeKindField = base::BitField8<EscapeKind, 0, 3>;
82using MayTerminateStringField = EscapeKindField::Next<bool, 1>;
83using NumberPartField = MayTerminateStringField::Next<bool, 1>;
84
85constexpr bool MayTerminateJsonString(uint8_t flags) {
86 return MayTerminateStringField::decode(flags);
87}
88
89constexpr EscapeKind GetEscapeKind(uint8_t flags) {
90 return EscapeKindField::decode(flags);
91}
92
93constexpr bool IsNumberPart(uint8_t flags) {
94 return NumberPartField::decode(flags);
95}
96
97constexpr uint8_t GetJsonScanFlags(uint8_t c) {
98 // clang-format off
99 return (c == 'b' ? EscapeKindField::encode(EscapeKind::kBackspace)
100 : c == 't' ? EscapeKindField::encode(EscapeKind::kTab)
101 : c == 'n' ? EscapeKindField::encode(EscapeKind::kNewLine)
102 : c == 'f' ? EscapeKindField::encode(EscapeKind::kFormFeed)
103 : c == 'r' ? EscapeKindField::encode(EscapeKind::kCarriageReturn)
104 : c == 'u' ? EscapeKindField::encode(EscapeKind::kUnicode)
105 : c == '"' ? EscapeKindField::encode(EscapeKind::kSelf)
106 : c == '\\' ? EscapeKindField::encode(EscapeKind::kSelf)
107 : c == '/' ? EscapeKindField::encode(EscapeKind::kSelf)
108 : EscapeKindField::encode(EscapeKind::kIllegal)) |
109 (c < 0x20 ? MayTerminateStringField::encode(true)
110 : c == '"' ? MayTerminateStringField::encode(true)
111 : c == '\\' ? MayTerminateStringField::encode(true)
112 : MayTerminateStringField::encode(false)) |
113 NumberPartField::encode(c == '.' ||
114 c == 'e' ||
115 c == 'E' ||
116 IsDecimalDigit(c) ||
117 c == '-' ||
118 c == '+');
119 // clang-format on
120}
121
122// Table of one-character scan flags, by character (0x00..0xFF only).
123static const constexpr uint8_t character_json_scan_flags[256] = {
124#define CALL_GET_SCAN_FLAGS(N) GetJsonScanFlags(N),
126#undef CALL_GET_SCAN_FLAGS
127#define CALL_GET_SCAN_FLAGS(N) GetJsonScanFlags(128 + N),
129#undef CALL_GET_SCAN_FLAGS
130};
131
132} // namespace
133
136 Handle<String> source, MaybeHandle<Object> val_node) {
137 DCHECK(IsCallable(*reviver));
138 JsonParseInternalizer internalizer(isolate, Cast<JSReceiver>(reviver),
139 source);
141 isolate->factory()->NewJSObject(isolate->object_function());
142 DirectHandle<String> name = isolate->factory()->empty_string();
143 JSObject::AddProperty(isolate, holder, name, result, NONE);
144 return internalizer.InternalizeJsonProperty<kWithSource>(
145 holder, name, val_node.ToHandleChecked(), result);
146}
147
148template <JsonParseInternalizer::WithOrWithoutSource with_source>
151 Handle<Object> val_node, DirectHandle<Object> snapshot) {
152 DCHECK_EQ(with_source == kWithSource,
153 !val_node.is_null() && !snapshot.is_null());
154 DCHECK(IsCallable(*reviver_));
155 HandleScope outer_scope(isolate_);
158 isolate_, value, Object::GetPropertyOrElement(isolate_, holder, name));
159
160 // When with_source == kWithSource, the source text is passed to the reviver
161 // if the reviver has not mucked with the originally parsed value.
162 //
163 // When with_source == kWithoutSource, this is unused.
164 bool pass_source_to_reviver =
165 with_source == kWithSource && Object::SameValue(*value, *snapshot);
166
167 if (IsJSReceiver(*value)) {
168 Handle<JSReceiver> object = Cast<JSReceiver>(value);
169 Maybe<bool> is_array = Object::IsArray(object);
170 if (is_array.IsNothing()) return MaybeHandle<Object>();
171 if (is_array.FromJust()) {
172 DirectHandle<Object> length_object;
174 isolate_, length_object,
176 double length = Object::NumberValue(*length_object);
177 if (pass_source_to_reviver) {
178 auto val_nodes_and_snapshots = Cast<FixedArray>(val_node);
179 int snapshot_length = val_nodes_and_snapshots->length() / 2;
180 for (int i = 0; i < length; i++) {
181 HandleScope inner_scope(isolate_);
183 Handle<String> index_name =
185 // Even if the array pointer snapshot matched, it's possible the
186 // array had new elements added that are not in the snapshotted
187 // elements.
188 const bool rv =
189 i < snapshot_length
191 object, index_name,
192 handle(val_nodes_and_snapshots->get(i * 2), isolate_),
193 handle(val_nodes_and_snapshots->get(i * 2 + 1),
194 isolate_))
196 object, index_name, Handle<Object>(), Handle<Object>());
197 if (!rv) {
198 return MaybeHandle<Object>();
199 }
200 }
201 } else {
202 for (int i = 0; i < length; i++) {
203 HandleScope inner_scope(isolate_);
205 Handle<String> index_name =
208 object, index_name, Handle<Object>(), Handle<Object>())) {
209 return MaybeHandle<Object>();
210 }
211 }
212 }
213 } else {
220 if (pass_source_to_reviver) {
221 auto val_nodes_and_snapshots = Cast<ObjectTwoHashTable>(val_node);
222 for (int i = 0; i < contents->length(); i++) {
223 HandleScope inner_scope(isolate_);
225 auto property_val_node_and_snapshot =
226 val_nodes_and_snapshots->Lookup(isolate_, key_name);
227 Handle<Object> property_val_node(property_val_node_and_snapshot[0],
228 isolate_);
229 Handle<Object> property_snapshot(property_val_node_and_snapshot[1],
230 isolate_);
231 // Even if the object pointer snapshot matched, it's possible the
232 // object had new properties added that are not in the snapshotted
233 // contents.
234 const bool rv =
235 !IsTheHole(*property_snapshot)
237 object, key_name, property_val_node, property_snapshot)
239 object, key_name, Handle<Object>(), Handle<Object>());
240 if (!rv) {
241 return MaybeHandle<Object>();
242 }
243 }
244 } else {
245 for (int i = 0; i < contents->length(); i++) {
246 HandleScope inner_scope(isolate_);
249 object, key_name, Handle<Object>(), Handle<Object>())) {
250 return MaybeHandle<Object>();
251 }
252 }
253 }
254 }
255 }
256
257 DirectHandle<JSObject> context =
258 isolate_->factory()->NewJSObject(isolate_->object_function());
259 if (pass_source_to_reviver && IsString(*val_node)) {
261 isolate_->factory()->source_string(),
262 val_node, Just(kThrowOnError))
263 .Check();
264 }
265 DirectHandle<Object> args[] = {name, value, context};
270 return outer_scope.CloseAndEscape(result);
271}
272
273template <JsonParseInternalizer::WithOrWithoutSource with_source>
275 Handle<String> name,
276 Handle<Object> val_node,
277 Handle<Object> snapshot) {
278 STACK_CHECK(isolate_, false);
279 DCHECK(IsCallable(*reviver_));
283 InternalizeJsonProperty<with_source>(holder, name, val_node, snapshot),
284 false);
285 Maybe<bool> change_result = Nothing<bool>();
286 if (IsUndefined(*result, isolate_)) {
287 change_result = JSReceiver::DeletePropertyOrElement(isolate_, holder, name,
289 } else {
292 desc.set_configurable(true);
293 desc.set_enumerable(true);
294 desc.set_writable(true);
295 change_result = JSReceiver::DefineOwnProperty(isolate_, holder, name, &desc,
297 }
298 MAYBE_RETURN(change_result, false);
299 return true;
300}
301
302template <typename Char>
304 : isolate_(isolate),
305 hash_seed_(HashSeed(isolate)),
306 object_constructor_(isolate_->object_function()),
307 original_source_(source) {
308 size_t start = 0;
309 size_t length = source->length();
310 PtrComprCageBase cage_base(isolate);
311 if (IsSlicedString(*source, cage_base)) {
313 start = string->offset();
314 Tagged<String> parent = string->parent();
315 if (IsThinString(parent, cage_base))
316 parent = Cast<ThinString>(parent)->actual();
317 source_ = handle(parent, isolate);
318 } else {
319 source_ = String::Flatten(isolate, source);
320 }
321
322 if (StringShape(*source_, cage_base).IsExternal()) {
323 chars_ =
324 static_cast<const Char*>(Cast<SeqExternalString>(*source_)->GetChars());
325 chars_may_relocate_ = false;
326 } else {
328 isolate->main_thread_local_heap()->AddGCEpilogueCallback(
330 chars_ = Cast<SeqString>(*source_)->GetChars(no_gc);
331 chars_may_relocate_ = true;
332 }
333 cursor_ = chars_ + start;
334 end_ = cursor_ + length;
335}
336
337template <typename Char>
339 // The special cases are undefined, NaN, Infinity, and {} being passed to the
340 // parse method
341 int offset = IsSlicedString(*original_source_)
342 ? Cast<SlicedString>(*original_source_)->offset()
343 : 0;
344 size_t length = original_source_->length();
345#define CASES(V) \
346 V("[object Object]") \
347 V("undefined") \
348 V("Infinity") \
349 V("NaN")
350 switch (length) {
351#define CASE(n) \
352 case arraysize(n) - 1: \
353 return CompareCharsEqual(chars_ + offset, n, arraysize(n) - 1);
354 CASES(CASE)
355 default:
356 return false;
357 }
358#undef CASE
359#undef CASES
360}
361
362template <typename Char>
365 MessageTemplate message;
366 Factory* factory = this->factory();
368 int origin_source_length = original_source_->length();
369 // Only provide context for strings with at least
370 // kMinOriginalSourceLengthForContext characters in length.
371 if (origin_source_length >= kMinOriginalSourceLengthForContext) {
372 int substring_start = 0;
373 int substring_end = origin_source_length;
374 if (pos < kMaxContextCharacters) {
375 message =
376 MessageTemplate::kJsonParseUnexpectedTokenStartStringWithContext;
377 // Output the string followed by ellipses.
378 substring_end = pos + kMaxContextCharacters;
379 } else if (pos >= kMaxContextCharacters &&
380 pos < origin_source_length - kMaxContextCharacters) {
381 message =
382 MessageTemplate::kJsonParseUnexpectedTokenSurroundStringWithContext;
383 // Add context before and after position of bad token surrounded by
384 // ellipses.
385 substring_start = pos - kMaxContextCharacters;
386 substring_end = pos + kMaxContextCharacters;
387 } else {
388 message = MessageTemplate::kJsonParseUnexpectedTokenEndStringWithContext;
389 // Add ellipses followed by some context before bad token.
390 substring_start = pos - kMaxContextCharacters;
391 }
392 arg2 =
393 factory->NewSubString(original_source_, substring_start, substring_end);
394 } else {
395 arg2 = original_source_;
396 // Output the entire string without ellipses but provide the token which
397 // was unexpected.
398 message = MessageTemplate::kJsonParseUnexpectedTokenShortString;
399 }
400 return message;
401}
402
403template <typename Char>
406 int pos) {
407 MessageTemplate message;
408 switch (token) {
409 case JsonToken::EOS:
410 message = MessageTemplate::kJsonParseUnexpectedEOS;
411 break;
413 message = MessageTemplate::kJsonParseUnexpectedTokenNumber;
414 break;
416 message = MessageTemplate::kJsonParseUnexpectedTokenString;
417 break;
418 default:
419 // Output entire string without ellipses and don't provide the token
420 // that was unexpected because it makes the error messages more confusing
421 if (IsSpecialString()) {
422 arg = original_source_;
423 message = MessageTemplate::kJsonParseShortString;
424 } else {
425 message = GetErrorMessageWithEllipses(arg, arg2, pos);
426 }
427 }
428 return message;
429}
430
431template <typename Char>
433 DirectHandle<Object>& column) {
434 // JSON allows only \r and \n as line terminators.
435 // (See https://www.json.org/json-en.html - "whitespace")
436 int line_number = 1;
437 const Char* start =
438 chars_ + (IsSlicedString(*original_source_)
439 ? Cast<SlicedString>(*original_source_)->offset()
440 : 0);
441 const Char* last_line_break = start;
442 const Char* cursor = start;
443 const Char* end = cursor_; // cursor_ points to the position of the error.
444 for (; cursor < end; ++cursor) {
445 if (*cursor == '\r' && cursor < end - 1 && cursor[1] == '\n') {
446 // \r\n counts as a single line terminator, as of
447 // https://tc39.es/ecma262/#sec-line-terminators. JSON itself does not
448 // have a notion of lines or line terminators.
449 ++cursor;
450 }
451 if (*cursor == '\r' || *cursor == '\n') {
452 ++line_number;
453 last_line_break = cursor + 1;
454 }
455 }
456 int column_number = 1 + static_cast<int>(cursor - last_line_break);
457 line = direct_handle(Smi::FromInt(line_number), isolate());
458 column = direct_handle(Smi::FromInt(column_number), isolate());
459}
460
461template <typename Char>
463 JsonToken token, std::optional<MessageTemplate> errorMessage) {
464 // Some exception (for example stack overflow) was already thrown.
465 if (isolate_->has_exception()) return;
466
467 // Parse failed. Current character is the unexpected token.
468 Factory* factory = this->factory();
469 int offset = IsSlicedString(*original_source_)
470 ? Cast<SlicedString>(*original_source_)->offset()
471 : 0;
472 int pos = position() - offset;
476 CalculateFileLocation(arg2, arg3);
477
478 MessageTemplate message =
479 errorMessage ? errorMessage.value()
480 : LookUpErrorMessageForJsonToken(token, arg, arg2, pos);
481
482 Handle<Script> script(factory->NewScript(original_source_));
483 DCHECK_IMPLIES(isolate_->NeedsSourcePositions(), script->has_line_ends());
485 if (!it.done() && it.is_javascript()) {
486 FrameSummary summary = it.GetTopValidFrame();
487 script->set_eval_from_shared(summary.AsJavaScript().function()->shared());
488 if (IsScript(*summary.script())) {
489 script->set_origin_options(
490 Cast<Script>(*summary.script())->origin_options());
491 }
492 }
493
494 // We should sent compile error event because we compile JSON object in
495 // separated source file.
496 isolate()->debug()->OnCompileError(script);
497 MessageLocation location(script, pos, pos + 1);
498 isolate()->ThrowAt(factory->NewSyntaxError(message, arg, arg2, arg3),
499 &location);
500
501 // Move the cursor to the end so we won't be able to proceed parsing.
502 cursor_ = end_;
503}
504
505template <typename Char>
508 if (c == kEndOfString) {
509 token = JsonToken::EOS;
510 } else if (c <= unibrow::Latin1::kMaxChar) {
511 token = one_char_json_tokens[c];
512 }
513 return ReportUnexpectedToken(token);
514}
515
516template <typename Char>
518 if (StringShape(*source_).IsExternal()) {
519 // Check that the string shape hasn't changed. Otherwise our GC hooks are
520 // broken.
522 } else {
523 // Check that the string shape hasn't changed. Otherwise our GC hooks are
524 // broken.
527 UpdatePointersCallback, this);
528 }
529}
530
531template <typename Char>
534 // Only record the val node when reviver is callable.
535 bool reviver_is_callable = IsCallable(*reviver);
536 bool should_track_json_source = reviver_is_callable;
537 if (V8_UNLIKELY(should_track_json_source)) {
538 ASSIGN_RETURN_ON_EXCEPTION(isolate(), result, ParseJsonValue<true>());
539 } else {
540 ASSIGN_RETURN_ON_EXCEPTION(isolate(), result, ParseJsonValueRecursive());
541 }
542
543 if (!Check(JsonToken::EOS)) {
544 ReportUnexpectedToken(
545 peek(), MessageTemplate::kJsonParseUnexpectedNonWhiteSpaceCharacter);
546 return MaybeHandle<Object>();
547 }
548 if (isolate_->has_exception()) {
549 return MaybeHandle<Object>();
550 }
551 return result;
552}
553
556
557namespace {
558template <typename Char>
559JsonToken GetTokenForCharacter(Char c) {
560 return V8_LIKELY(c <= unibrow::Latin1::kMaxChar) ? one_char_json_tokens[c]
562}
563} // namespace
564
565template <typename Char>
567 JsonToken local_next = JsonToken::EOS;
568
569 cursor_ = std::find_if(cursor_, end_, [&](Char c) {
570 JsonToken current = GetTokenForCharacter(c);
571 bool result = current != JsonToken::WHITESPACE;
572 if (V8_LIKELY(result)) local_next = current;
573 return result;
574 });
575
576 next_ = local_next;
577}
578
579template <typename Char>
581 base::uc32 value = 0;
582 for (int i = 0; i < 4; i++) {
583 int digit = base::HexValue(NextCharacter());
584 if (V8_UNLIKELY(digit < 0)) return kInvalidUnicodeCharacter;
585 value = value * 16 + digit;
586 }
587 return value;
588}
589
590// Parse any JSON value.
591template <typename Char>
593 {
595 const Char* start = cursor_;
596 base::uc32 first = CurrentCharacter();
597 if (first == '\\' && NextCharacter() == 'u') first = ScanUnicodeCharacter();
598 if (IsDecimalDigit(first)) {
599 if (first == '0') {
600 if (NextCharacter() == '"') {
601 advance();
602 // Record element information.
603 cont->elements++;
604 DCHECK_LE(0, cont->max_index);
605 return JsonString(0);
606 }
607 } else {
608 uint32_t index = first - '0';
609 while (true) {
610 cursor_ = std::find_if(cursor_ + 1, end_, [&index](Char c) {
611 return !IsDecimalDigit(c) || !TryAddArrayIndexChar(&index, c);
612 });
613
614 if (CurrentCharacter() == '"') {
615 advance();
616 // Record element information.
617 cont->elements++;
618 cont->max_index = std::max(cont->max_index, index);
619 return JsonString(index);
620 }
621
622 if (CurrentCharacter() == '\\' && NextCharacter() == 'u') {
623 base::uc32 c = ScanUnicodeCharacter();
624 if (IsDecimalDigit(c) && TryAddArrayIndexChar(&index, c)) continue;
625 }
626
627 break;
628 }
629 }
630 }
631 // Reset cursor_ to start if the key is not an index.
632 cursor_ = start;
633 }
634 return ScanJsonString(true);
635}
636
638 public:
639 // TODO(leszeks): If allocation alignment is ever enabled, we'll need to add
640 // padding fillers between heap numbers.
641 static_assert(!USE_ALLOCATION_ALIGNMENT_BOOL);
642
644 if (count == 0) return;
645 int size = count * sizeof(HeapNumber);
646 raw_bytes_ = isolate->factory()->NewByteArray(size);
647 }
648
650
651 private:
653};
654
656 public:
658 Isolate* isolate, FoldedMutableHeapNumberAllocation* allocation,
660 : isolate_(isolate), roots_(isolate) {
661 if (allocation->raw_bytes().is_null()) return;
662
663 raw_bytes_ = allocation->raw_bytes();
665 reinterpret_cast<Address>(allocation->raw_bytes()->begin());
666 }
667
669 // Make all mutable HeapNumbers alive.
670 if (mutable_double_address_ == 0) {
671 DCHECK(raw_bytes_.is_null());
672 return;
673 }
674
676 reinterpret_cast<Address>(raw_bytes_->end()));
677 // Before setting the length of mutable_double_buffer back to zero, we
678 // must ensure that the sweeper is not running or has already swept the
679 // object's page. Otherwise the GC can add the contents of
680 // mutable_double_buffer to the free list.
682 raw_bytes_->set_length(0);
683 }
684
687 reinterpret_cast<Address>(raw_bytes_->begin()));
689 hn->set_map_after_allocation(isolate_, roots.heap_number_map());
690 Cast<HeapNumber>(hn)->set_value_as_bits(value.get_bits());
694 reinterpret_cast<Address>(raw_bytes_->end()));
695 return Cast<HeapNumber>(hn);
696 }
697
698 private:
703};
704
705// JSDataObjectBuilder is a helper for efficiently building a data object,
706// similar (in semantics and efficiency) to a JS object literal, based on
707// key/value pairs.
708//
709// The JSDataObjectBuilder works by first trying to find the right map for the
710// object, and then letting the caller stamp out the object fields linearly.
711// There are several fast paths that can be fallen out of; if the builder bails
712// out, then it's still possible to stamp out the object partially based on the
713// last map found, and then continue with slow object setup afterward.
714//
715// The maps start from the object literal cache (to try to share maps with
716// equivalent object literals in JS code). From there, when adding properties,
717// there are several fast paths that the builder follows:
718//
719// 1. At construction, it can be passed an expected final map for the object
720// (e.g. cached from previous runs, or assumed from surrounding objects).
721// If given, then we first check whether the property matches the
722// entry in the DescriptorArray of the final map; if yes, then we don't
723// need to do any map transitions.
724// 2. When given a property key, it looks for whether there is exactly one
725// transition away from the current map ("ExpectedTransition").
726// The expected key is passed as a hint to the current property key
727// getter, for e.g. faster internalized string materialization.
728// 3. Otherwise, it searches for whether there is any transition in the
729// current map that matches the key.
730// 4. For all of the above, it checks whether the field representation of the
731// found map matches the representation of the value. If it doesn't, it
732// migrates the map, potentially deprecating it too.
733// 5. If there is no transition, it tries to allocate a new map transition,
734// bailing out if this fails.
736 public:
737 // HeapNumberMode determines whether incoming HeapNumber values will be
738 // guaranteed to be uniquely owned by this object, and therefore can be used
739 // directly as mutable HeapNumbers for double representation fields.
744 JSDataObjectBuilder(Isolate* isolate, ElementsKind elements_kind,
745 int expected_named_properties,
746 DirectHandle<Map> expected_final_map,
747 HeapNumberMode heap_number_mode)
748 : isolate_(isolate),
749 elements_kind_(elements_kind),
750 expected_property_count_(expected_named_properties),
751 heap_number_mode_(heap_number_mode),
752 expected_final_map_(expected_final_map) {
755 }
756 }
757
758 // Builds and returns an object whose properties are based on a property
759 // iterator.
760 //
761 // Expects an iterator of the form:
762 //
763 // struct Iterator {
764 // void Advance();
765 // bool Done();
766 //
767 // // Get the key of the current property, optionally returning the hinted
768 // // expected key if applicable.
769 // Handle<String> GetKey(Handle<String> expected_key_hint);
770 //
771 // // Get the value of the current property. `will_revisit_value` is true
772 // // if this value will need to be revisited later via RevisitValues().
773 // Handle<Object> GetValue(bool will_revisit_value);
774 //
775 // // Return an iterator over the values that were already visited by
776 // // GetValue. Might require caching those values if necessary.
777 // ValueIterator RevisitValues();
778 // }
779 template <typename PropertyIterator>
781 PropertyIterator&& it, MaybeHandle<FixedArrayBase> maybe_elements = {}) {
782 Handle<String> failed_property_add_key;
783 for (; !it.Done(); it.Advance()) {
784 Handle<String> property_key;
786 it.GetKeyChars(),
787 [&](Handle<String> expected_key) {
788 return property_key = it.GetKey(expected_key);
789 },
790 [&]() { return it.GetValue(true); })) {
791 failed_property_add_key = property_key;
792 break;
793 }
794 }
795
796 DirectHandle<FixedArrayBase> elements;
797 if (!maybe_elements.ToHandle(&elements)) {
798 elements = isolate_->factory()->empty_fixed_array();
799 }
800 CreateAndInitialiseObject(it.RevisitValues(), elements);
801
802 // Slow path: define remaining named properties.
803 for (; !it.Done(); it.Advance()) {
804 DirectHandle<String> key;
805 if (!failed_property_add_key.is_null()) {
806 key = std::exchange(failed_property_add_key, {});
807 } else {
808 key = it.GetKey({});
809 }
810#ifdef DEBUG
811 uint32_t index;
812 DCHECK(!key->AsArrayIndex(&index));
813#endif
814 Handle<Object> value = it.GetValue(false);
815 AddSlowProperty(key, value);
816 }
817
818 return object();
819 }
820
821 template <typename Char, typename GetKeyFunction, typename GetValueFunction>
823 GetKeyFunction&& get_key,
824 GetValueFunction&& get_value) {
825 // The fast path is only valid as long as we haven't allocated an object
826 // yet.
827 DCHECK(object_.is_null());
828
830 bool existing_map_found =
831 TryFastTransitionToPropertyKey(key_chars, get_key, &key);
832 // Unconditionally get the value after getting the transition result.
833 DirectHandle<Object> value = get_value();
834 if (existing_map_found) {
835 // We found a map with a field for our value -- now make sure that field
836 // is compatible with our value.
837 if (!TryGeneralizeFieldToValue(value)) {
838 // TODO(leszeks): Try to stay on the fast path if we just deprecate
839 // here.
840 return false;
841 }
843 return true;
844 }
845
846 // Try to stay on a semi-fast path (being able to stamp out the object
847 // fields after creating the correct map) by manually creating the next
848 // map here.
849
850 Tagged<DescriptorArray> descriptors = map_->instance_descriptors(isolate_);
851 InternalIndex descriptor_number =
852 descriptors->SearchWithCache(isolate_, *key, *map_);
853 if (descriptor_number.is_found()) {
854 // Duplicate property, we need to bail out of even the semi-fast path
855 // because we can no longer stamp out values linearly.
856 return false;
857 }
858
860 return false;
861 }
862
863 Representation representation =
866 Object::OptimalType(*value, isolate_, representation);
869 representation, INSERT_TRANSITION);
870 Handle<Map> next_map;
871 if (!maybe_map.ToHandle(&next_map)) return false;
872 if (next_map->is_dictionary_map()) return false;
873
874 map_ = next_map;
875 if (representation.IsDouble()) {
877 }
879 return true;
880 }
881
882 template <typename ValueIterator>
884 ValueIterator value_it, DirectHandle<FixedArrayBase> elements) {
885 // We've created a map for the first `i` property stack values (which might
886 // be all of them). We need to write these properties to a newly allocated
887 // object.
888 DCHECK(object_.is_null());
889
891 // If we were on the expected map fast path all the way, but never reached
892 // the expected final map itself, then finalize the map by rewinding to
893 // the one whose property is the actual current property index.
894 //
895 // TODO(leszeks): Do we actually want to use the final map fast path when
896 // we know that the current map _can't_ reach the final map? Will we even
897 // hit this case given that we check for matching instance size?
899 }
900
901 if (map_->is_dictionary_map()) {
902 // It's only safe to emit a dictionary map when we've not set up any
903 // properties, as the caller assumes it can set up the first N properties
904 // as fast data properties.
906
909 object->set_elements(*elements);
910 object_ = object;
911 return;
912 }
913
914 // The map should have as many own descriptors as the number of properties
915 // we've created so far...
916 DCHECK_EQ(current_property_index_, map_->NumberOfOwnDescriptors());
917
918 // ... and all of those properties should be in-object data properties.
920 map_->GetInObjectProperties() - map_->UnusedInObjectProperties());
921
922 // Create a folded mutable HeapNumber allocation area before allocating the
923 // object -- this ensures that there is no allocation between the object
924 // allocation and its initial fields being initialised, where the verifier
925 // would see invalid double field state.
928
929 // Allocate the object then immediately start a no_gc scope -- again, this
930 // is so the verifier doesn't see invalid double field state.
933 Tagged<JSObject> raw_object = *object;
934
935 raw_object->set_elements(*elements);
936 Tagged<DescriptorArray> descriptors =
937 raw_object->map()->instance_descriptors();
938
939 WriteBarrierMode mode = raw_object->GetWriteBarrierMode(no_gc);
940 FoldedMutableHeapNumberAllocator hn_allocator(isolate_, &hn_allocation,
941 no_gc);
942
943 ReadOnlyRoots roots(isolate_);
944
945 // Initialize the in-object properties up to the last added property.
946 int current_property_offset = raw_object->GetInObjectPropertyOffset(0);
947 for (int i = 0; i < current_property_index_; ++i, ++value_it) {
948 InternalIndex descriptor_index(i);
949 Tagged<Object> value = **value_it;
950
951 // See comment in RegisterFieldNeedsFreshHeapNumber, we need to allocate
952 // HeapNumbers for double representation fields when we can't make
953 // existing HeapNumbers mutable, or when we only have a Smi value.
955 IsSmi(value)) {
956 PropertyDetails details = descriptors->GetDetails(descriptor_index);
957 if (details.representation().IsDouble()) {
958 value = hn_allocator.AllocateNext(
959 roots, Float64(Object::NumberValue(value)));
960 }
961 }
962
964 DCHECK_EQ(current_property_offset,
965 FieldIndex::ForPropertyIndex(object->map(), i).offset());
966 DCHECK_EQ(current_property_offset,
967 object->map()->GetInObjectPropertyOffset(i));
968 FieldIndex index = FieldIndex::ForInObjectOffset(current_property_offset,
970 raw_object->RawFastInobjectPropertyAtPut(index, value, mode);
971 current_property_offset += kTaggedSize;
972 }
973 DCHECK_EQ(current_property_offset, object->map()->GetInObjectPropertyOffset(
975
976 object_ = object;
977 }
978
985
987 DCHECK(!object_.is_null());
988 return object_;
989 }
990
991 private:
992 template <typename Char, typename GetKeyFunction>
994 base::Vector<const Char> key_chars, GetKeyFunction&& get_key,
995 Handle<String>* key_out) {
996 Handle<String> expected_key;
997 DirectHandle<Map> target_map;
998
999 InternalIndex descriptor_index(current_property_index_);
1001 expected_key = handle(
1003 expected_final_map_->instance_descriptors(isolate_)->GetKey(
1004 descriptor_index)),
1005 isolate_);
1006 target_map = expected_final_map_;
1007 } else {
1008 TransitionsAccessor transitions(isolate_, *map_);
1009 auto expected_transition = transitions.ExpectedTransition(key_chars);
1010 if (!expected_transition.first.is_null()) {
1011 // Directly read out the target while reading out the key, otherwise it
1012 // might die if `get_key` can allocate.
1013 target_map = expected_transition.second;
1014
1015 // We were successful and we are done.
1016 DCHECK_EQ(target_map->instance_descriptors()
1017 ->GetDetails(descriptor_index)
1018 .location(),
1020 map_ = target_map;
1021 return true;
1022 }
1023 }
1024
1025 DirectHandle<String> key = *key_out = get_key(expected_key);
1026 if (key.is_identical_to(expected_key)) {
1027 // We were successful and we are done.
1028 DCHECK_EQ(target_map->instance_descriptors()
1029 ->GetDetails(descriptor_index)
1030 .location(),
1032 map_ = target_map;
1033 return true;
1034 }
1035
1037 // We were on the expected map fast path, but this missed that fast
1038 // path, so rewind the optimistic setting of the current map and disable
1039 // this fast path.
1042 }
1043
1044 MaybeHandle<Map> maybe_target =
1046 if (!maybe_target.ToHandle(&target_map)) return false;
1047
1048 map_ = target_map;
1049 return true;
1050 }
1051
1053 DCHECK_LT(current_property_index_, map_->NumberOfOwnDescriptors());
1054
1055 InternalIndex descriptor_index(current_property_index_);
1056 PropertyDetails current_details =
1057 map_->instance_descriptors(isolate_)->GetDetails(descriptor_index);
1058 Representation expected_representation = current_details.representation();
1059
1060 DCHECK_EQ(current_details.kind(), PropertyKind::kData);
1061 DCHECK_EQ(current_details.location(), PropertyLocation::kField);
1062
1063 if (!Object::FitsRepresentation(*value, expected_representation)) {
1064 Representation representation =
1066 representation = representation.generalize(expected_representation);
1067 if (!expected_representation.CanBeInPlaceChangedTo(representation)) {
1068 // Reconfigure the map for the value, deprecating if necessary. This
1069 // will only happen for double representation fields.
1071 // If we're on the fast path, we will have advanced the current map
1072 // all the way to the final expected map. Make sure to rewind to the
1073 // "real" current map if this happened.
1074 //
1075 // An alternative would be to deprecate the expected final map,
1076 // migrate it to the new representation, and stay on the fast path.
1077 // However, this would mean allocating all-new maps (with the new
1078 // representation) all the way between the current map and the new
1079 // expected final map; if we later fall off the fast path anyway, then
1080 // all those newly allocated maps will end up unused.
1083 }
1085 Handle<Map> new_map = mu.ReconfigureToDataField(
1086 descriptor_index, current_details.attributes(),
1087 current_details.constness(), representation,
1089
1090 // We only want to stay on the fast path if we got a fast map.
1091 if (new_map->is_dictionary_map()) return false;
1092 map_ = new_map;
1093 DCHECK(representation.IsDouble());
1095 } else {
1096 // Do the in-place reconfiguration.
1097 DCHECK(!representation.IsDouble());
1098 DirectHandle<FieldType> value_type =
1099 Object::OptimalType(*value, isolate_, representation);
1100 MapUpdater::GeneralizeField(isolate_, map_, descriptor_index,
1101 current_details.constness(), representation,
1102 value_type);
1103 }
1104 } else if (expected_representation.IsHeapObject() &&
1106 map_->instance_descriptors(isolate_)->GetFieldType(
1107 descriptor_index),
1108 value)) {
1109 DirectHandle<FieldType> value_type =
1110 Object::OptimalType(*value, isolate_, expected_representation);
1111 MapUpdater::GeneralizeField(isolate_, map_, descriptor_index,
1112 current_details.constness(),
1113 expected_representation, value_type);
1114 } else if (expected_representation.IsDouble()) {
1116 }
1117
1119 map_->instance_descriptors(isolate_)->GetFieldType(descriptor_index),
1120 value));
1121 return true;
1122 }
1123
1125 if (expected_final_map_.is_null()) return false;
1126 if (expected_final_map_->elements_kind() != elements_kind_) return false;
1127
1128 int property_count_in_expected_final_map =
1129 expected_final_map_->NumberOfOwnDescriptors();
1130 if (property_count_in_expected_final_map < expected_property_count_)
1131 return false;
1132
1135 property_count_in_expected_final_map;
1136 return true;
1137 }
1138
1140 // Must be called before any properties are registered.
1142
1147 } else {
1148 DCHECK_EQ(map_->elements_kind(), elements_kind_);
1149 }
1150 }
1151
1157
1160 if (current_property_index_ == 0) {
1162 DCHECK_EQ(0, map_->NumberOfOwnDescriptors());
1163 }
1164 if (current_property_index_ == 0) {
1165 return;
1166 }
1168 map_ = handle(map_->FindFieldOwner(
1170 isolate_);
1171 }
1172
1179
1181 // We need to allocate a new HeapNumber for double representation fields if
1182 // the HeapNumber values is not guaranteed to be uniquely owned by this
1183 // object (and therefore can't be made mutable), or if the value is a Smi
1184 // and there is no HeapNumber box for this value yet at all.
1186 !IsSmi(*value)) {
1187 DCHECK(IsHeapNumber(*value));
1188 return;
1189 }
1191 }
1192
1194
1199
1203
1205
1208};
1209
1211 public:
1213 : it_(it), end_(end) {
1214 DCHECK_LE(it_, end_);
1215 DCHECK_IMPLIES(it_ != end_, !it_->string.is_index());
1216 }
1217
1219 DCHECK_LT(it_, end_);
1220 do {
1221 it_++;
1222 } while (it_ != end_ && it_->string.is_index());
1223 return *this;
1224 }
1225
1227
1228 bool operator!=(const NamedPropertyValueIterator& other) const {
1229 return it_ != other.it_;
1230 }
1231
1232 private:
1233 // We need to store both the current iterator and the iterator end, since we
1234 // don't want to iterate past the end on operator++ if the last property is an
1235 // index property.
1238};
1239
1240template <typename Char>
1242 public:
1244 const JsonProperty* end)
1245 : parser_(parser), it_(it), end_(end) {
1246 DCHECK_LE(it_, end_);
1247 while (it_ != end_ && it_->string.is_index()) {
1248 it_++;
1249 }
1250 start_ = it_;
1251 }
1252
1253 void Advance() {
1254 DCHECK_LT(it_, end_);
1255 do {
1256 it_++;
1257 } while (it_ != end_ && it_->string.is_index());
1258 }
1259
1260 bool Done() const {
1261 DCHECK_LE(it_, end_);
1262 return it_ == end_;
1263 }
1264
1266 return parser_.GetKeyChars(it_->string);
1267 }
1269 return parser_.MakeString(it_->string, expected_key_hint);
1270 }
1271 Handle<Object> GetValue(bool will_revisit_value) {
1272 // Revisiting values is free, so we don't need to cache the value anywhere.
1273 return it_->value;
1274 }
1278
1279 private:
1281
1285};
1286
1287template <typename Char>
1288template <bool should_track_json_source>
1290 DirectHandle<Map> feedback) {
1291 if (!feedback.is_null() && feedback->is_deprecated()) {
1292 feedback = Map::Update(isolate_, feedback);
1293 }
1294 size_t start = cont.index;
1295 DCHECK_LE(start, property_stack_.size());
1296 int length = static_cast<int>(property_stack_.size() - start);
1297 int named_length = length - cont.elements;
1298 DCHECK_LE(0, named_length);
1299
1300 Handle<FixedArrayBase> elements;
1301 ElementsKind elements_kind = HOLEY_ELEMENTS;
1302
1303 // First store the elements.
1304 if (cont.elements > 0) {
1305 // Store as dictionary elements if that would use less memory.
1306 if (ShouldConvertToSlowElements(cont.elements, cont.max_index + 1)) {
1308 NumberDictionary::New(isolate_, cont.elements);
1309 for (int i = 0; i < length; i++) {
1310 const JsonProperty& property = property_stack_[start + i];
1311 if (!property.string.is_index()) continue;
1312 uint32_t index = property.string.index();
1313 DirectHandle<Object> value = property.value;
1314 NumberDictionary::UncheckedSet(isolate_, elms, index, value);
1315 }
1316 elms->SetInitialNumberOfElements(cont.elements);
1317 elms->UpdateMaxNumberKey(cont.max_index, Handle<JSObject>::null());
1318 elements_kind = DICTIONARY_ELEMENTS;
1319 elements = elms;
1320 } else {
1321 Handle<FixedArray> elms =
1322 factory()->NewFixedArrayWithHoles(cont.max_index + 1);
1324 Tagged<FixedArray> raw_elements = *elms;
1325 WriteBarrierMode mode = raw_elements->GetWriteBarrierMode(no_gc);
1326
1327 for (int i = 0; i < length; i++) {
1328 const JsonProperty& property = property_stack_[start + i];
1329 if (!property.string.is_index()) continue;
1330 uint32_t index = property.string.index();
1331 DirectHandle<Object> value = property.value;
1332 raw_elements->set(static_cast<int>(index), *value, mode);
1333 }
1334 elements = elms;
1335 }
1336 } else {
1337 elements = factory()->empty_fixed_array();
1338 }
1339
1340 // When tracking JSON source with a reviver, do not use mutable HeapNumbers.
1341 // In this mode, values are snapshotted at the beginning because the source is
1342 // only passed to the reviver if the reviver does not muck with the original
1343 // value. Mutable HeapNumbers would make the snapshot incorrect.
1344 JSDataObjectBuilder js_data_object_builder(
1345 isolate_, elements_kind, named_length, feedback,
1346 should_track_json_source
1349
1350 NamedPropertyIterator it(*this, property_stack_.begin() + start,
1351 property_stack_.end());
1352
1353 return js_data_object_builder.BuildFromIterator(it, elements);
1354}
1355
1356template <typename Char>
1358 int length = static_cast<int>(element_stack_.size() - start);
1359
1361 for (size_t i = start; i < element_stack_.size(); i++) {
1362 Tagged<Object> value = *element_stack_[i];
1363 if (IsHeapObject(value)) {
1364 if (IsHeapNumber(Cast<HeapObject>(value))) {
1366 } else {
1368 break;
1369 }
1370 }
1371 }
1372
1373 Handle<JSArray> array = factory()->NewJSArray(kind, length, length);
1376 Tagged<FixedDoubleArray> elements =
1377 Cast<FixedDoubleArray>(array->elements());
1378 for (int i = 0; i < length; i++) {
1379 elements->set(i, Object::NumberValue(*element_stack_[start + i]));
1380 }
1381 } else {
1383 Tagged<FixedArray> elements = Cast<FixedArray>(array->elements());
1386 : elements->GetWriteBarrierMode(no_gc);
1387 for (int i = 0; i < length; i++) {
1388 elements->set(i, *element_stack_[start + i], mode);
1389 }
1390 }
1391 return array;
1392}
1393
1394// Parse rawJSON value.
1395template <typename Char>
1397 if (end_ == cursor_) {
1398 isolate_->Throw(*isolate_->factory()->NewSyntaxError(
1399 MessageTemplate::kInvalidRawJsonValue));
1400 return false;
1401 }
1402 next_ = GetTokenForCharacter(*cursor_);
1403 switch (peek()) {
1404 case JsonToken::STRING:
1405 Consume(JsonToken::STRING);
1406 ScanJsonString(false);
1407 break;
1408
1409 case JsonToken::NUMBER:
1410 ParseJsonNumber();
1411 break;
1412
1414 ScanLiteral("true");
1415 break;
1416
1418 ScanLiteral("false");
1419 break;
1420
1422 ScanLiteral("null");
1423 break;
1424
1425 default:
1426 ReportUnexpectedCharacter(CurrentCharacter());
1427 return false;
1428 }
1429 if (isolate_->has_exception()) return false;
1430 if (cursor_ != end_) {
1431 isolate_->Throw(*isolate_->factory()->NewSyntaxError(
1432 MessageTemplate::kInvalidRawJsonValue));
1433 return false;
1434 }
1435 return true;
1436}
1437
1438template <typename Char>
1440 Handle<Map> feedback) {
1441 SkipWhitespace();
1442 switch (peek()) {
1443 case JsonToken::NUMBER:
1444 return ParseJsonNumber();
1445 case JsonToken::STRING:
1446 Consume(JsonToken::STRING);
1447 return MakeString(ScanJsonString(false));
1448
1450 ScanLiteral("true");
1451 return factory()->true_value();
1453 ScanLiteral("false");
1454 return factory()->false_value();
1456 ScanLiteral("null");
1457 return factory()->null_value();
1458
1459 case JsonToken::LBRACE:
1460 return ParseJsonObject(feedback);
1461 case JsonToken::LBRACK:
1462 return ParseJsonArray();
1463
1464 case JsonToken::COLON:
1465 case JsonToken::COMMA:
1466 case JsonToken::ILLEGAL:
1467 case JsonToken::RBRACE:
1468 case JsonToken::RBRACK:
1469 case JsonToken::EOS:
1470 ReportUnexpectedCharacter(CurrentCharacter());
1471 // Pop the continuation stack to correctly tear down handle scopes.
1472 return MaybeHandle<Object>();
1473
1475 UNREACHABLE();
1476 }
1477}
1478
1479template <typename Char>
1481 {
1483 if (V8_UNLIKELY(check.HasOverflowed())) {
1484 return ParseJsonValue<false>();
1485 }
1486 }
1487
1488 Consume(JsonToken::LBRACE);
1489 if (Check(JsonToken::RBRACE)) {
1490 return factory()->NewJSObject(object_constructor_);
1491 }
1492
1493 JsonContinuation cont(isolate_, JsonContinuation::kObjectProperty,
1494 property_stack_.size());
1495 bool first = true;
1496 do {
1497 ExpectNext(
1499 first ? MessageTemplate::kJsonParseExpectedPropNameOrRBrace
1500 : MessageTemplate::kJsonParseExpectedDoubleQuotedPropertyName);
1501 JsonString key = ScanJsonPropertyKey(&cont);
1502 ExpectNext(JsonToken::COLON,
1503 MessageTemplate::kJsonParseExpectedColonAfterPropertyName);
1505 if (V8_UNLIKELY(!ParseJsonValueRecursive().ToHandle(&value))) return {};
1506 property_stack_.emplace_back(key, value);
1507 first = false;
1508 } while (Check(JsonToken::COMMA));
1509
1510 Expect(JsonToken::RBRACE, MessageTemplate::kJsonParseExpectedCommaOrRBrace);
1511 Handle<Object> result = BuildJsonObject<false>(cont, feedback);
1512 property_stack_.resize(cont.index);
1513 return cont.scope.CloseAndEscape(result);
1514}
1515
1516template <typename Char>
1518 {
1520 if (V8_UNLIKELY(check.HasOverflowed())) {
1521 return ParseJsonValue<false>();
1522 }
1523 }
1524
1525 Consume(JsonToken::LBRACK);
1526 if (Check(JsonToken::RBRACK)) {
1527 return factory()->NewJSArray(0, PACKED_SMI_ELEMENTS);
1528 }
1529
1530 HandleScope handle_scope(isolate_);
1531 size_t start = element_stack_.size();
1532
1533 // Avoid allocating HeapNumbers until we really need to.
1534 SkipWhitespace();
1535 bool saw_double = false;
1536 bool success = false;
1537 DCHECK_EQ(double_elements_.size(), 0);
1538 DCHECK_EQ(smi_elements_.size(), 0);
1539 while (peek() == JsonToken::NUMBER) {
1540 double current_double;
1541 int current_smi;
1542 if (ParseJsonNumberAsDoubleOrSmi(&current_double, &current_smi)) {
1543 saw_double = true;
1544 double_elements_.push_back(current_double);
1545 } else {
1546 if (saw_double) {
1547 double_elements_.push_back(current_smi);
1548 } else {
1549 smi_elements_.push_back(current_smi);
1550 }
1551 }
1552 if (Check(JsonToken::COMMA)) {
1553 SkipWhitespace();
1554 continue;
1555 } else {
1556 Expect(JsonToken::RBRACK,
1557 MessageTemplate::kJsonParseExpectedCommaOrRBrack);
1558 success = true;
1559 break;
1560 }
1561 }
1562
1563 if (success) {
1564 // We managed to parse the whole array either as Smis or doubles. Empty
1565 // arrays are handled above, so here we will have some elements.
1566 DCHECK(smi_elements_.size() != 0 || double_elements_.size() != 0);
1567 int length =
1568 static_cast<int>(smi_elements_.size() + double_elements_.size());
1569 Handle<JSArray> array;
1570 if (!saw_double) {
1571 array = factory()->NewJSArray(PACKED_SMI_ELEMENTS, length, length);
1573 Tagged<FixedArray> elements = Cast<FixedArray>(array->elements());
1574 for (int i = 0; i < length; i++) {
1575 elements->set(i, Smi::FromInt(smi_elements_[i]));
1576 }
1577 } else {
1578 array = factory()->NewJSArray(PACKED_DOUBLE_ELEMENTS, length, length);
1580 Tagged<FixedDoubleArray> elements =
1581 Cast<FixedDoubleArray>(array->elements());
1582 int i = 0;
1583 for (int element : smi_elements_) {
1584 elements->set(i++, element);
1585 }
1586 for (double element : double_elements_) {
1587 elements->set(i++, element);
1588 }
1589 }
1590 smi_elements_.resize(0);
1591 double_elements_.resize(0);
1592 return handle_scope.CloseAndEscape(array);
1593 }
1594 // Otherwise, we fell out of the while loop above because the next element
1595 // is not a number. Move the smi_elements_ and double_elements_ to
1596 // element_stack_ and continue parsing the next element.
1597 for (int element : smi_elements_) {
1598 element_stack_.emplace_back(handle(Smi::FromInt(element), isolate_));
1599 }
1600 smi_elements_.resize(0);
1601 for (double element : double_elements_) {
1602 element_stack_.emplace_back(factory()->NewNumber(element));
1603 }
1604 double_elements_.resize(0);
1605
1607 if (V8_UNLIKELY(!ParseJsonValueRecursive().ToHandle(&value))) return {};
1608 element_stack_.emplace_back(value);
1609 while (Check(JsonToken::COMMA)) {
1610 Handle<Map> feedback;
1611 if (IsJSObject(*value)) {
1612 Tagged<Map> maybe_feedback = Cast<JSObject>(*value)->map();
1613 // Don't consume feedback from objects with a map that's detached
1614 // from the transition tree.
1615 if (!maybe_feedback->IsDetached(isolate_)) {
1616 feedback = handle(maybe_feedback, isolate_);
1617 }
1618 }
1619 if (V8_UNLIKELY(!ParseJsonValueRecursive(feedback).ToHandle(&value))) {
1620 return {};
1621 }
1622 element_stack_.emplace_back(value);
1623 }
1624
1625 Expect(JsonToken::RBRACK, MessageTemplate::kJsonParseExpectedCommaOrRBrack);
1626 Handle<Object> result = BuildJsonArray(start);
1627 element_stack_.resize(start);
1628 return handle_scope.CloseAndEscape(result);
1629}
1630
1631// Parse any JSON value.
1632template <typename Char>
1633template <bool should_track_json_source>
1635 std::vector<JsonContinuation> cont_stack;
1636
1637 cont_stack.reserve(16);
1638
1639 JsonContinuation cont(isolate_, JsonContinuation::kReturn, 0);
1640
1642
1643 // When should_track_json_source is true, we use val_node to record current
1644 // JSON value's parse node.
1645 //
1646 // For primitive values, the val_node is the source string of the JSON value.
1647 //
1648 // For JSObject values, the val_node is an ObjectHashTable in which the key is
1649 // the property name and the first value is the property value's parse
1650 // node. The order in which properties are defined may be different from the
1651 // order in which properties are enumerated when calling
1652 // InternalizeJSONProperty for the JSObject value. E.g., the JSON source
1653 // string is '{"a": 1, "1": 2}', and the properties enumerate order is ["1",
1654 // "a"]. Moreover, properties may be defined repeatedly in the JSON string.
1655 // E.g., the JSON string is '{"a": 1, "a": 1}', and the properties enumerate
1656 // order is ["a"]. So we cannot use the FixedArray to record the properties's
1657 // parse node by the order in which properties are defined and we use a
1658 // ObjectHashTable here to record the property name and the property's parse
1659 // node. We then look up the property's parse node by the property name when
1660 // calling InternalizeJSONProperty. The second value associated with the key
1661 // is the property value's snapshot.
1662 //
1663 // For JSArray values, the val_node is a FixedArray containing the parse nodes
1664 // and snapshots of the elements.
1665 //
1666 // For information about snapshotting, see below.
1667 Handle<Object> val_node;
1668 // Record the start position and end position for the primitive values.
1669 uint32_t start_position;
1670 uint32_t end_position;
1671
1672 // Workaround for -Wunused-but-set-variable on old gcc versions (version < 8).
1673 USE(start_position);
1674 USE(end_position);
1675
1676 // element_val_node_stack is used to track all the elements's
1677 // parse nodes. And we use this to construct the JSArray's
1678 // parse node and value snapshot.
1679 SmallVector<Handle<Object>> element_val_node_stack;
1680 // property_val_node_stack is used to track all the property
1681 // value's parse nodes. And we use this to construct the
1682 // JSObject's parse node and value snapshot.
1683 SmallVector<Handle<Object>> property_val_node_stack;
1684 while (true) {
1685 // Produce a json value.
1686 //
1687 // Iterate until a value is produced. Starting but not immediately finishing
1688 // objects and arrays will cause the loop to continue until a first member
1689 // is completed.
1690 while (true) {
1691 SkipWhitespace();
1692 // The switch is immediately followed by 'break' so we can use 'break' to
1693 // break out of the loop, and 'continue' to continue the loop.
1694
1695 if constexpr (should_track_json_source) {
1696 start_position = position();
1697 }
1698 switch (peek()) {
1699 case JsonToken::STRING:
1700 Consume(JsonToken::STRING);
1701 value = MakeString(ScanJsonString(false));
1702 if constexpr (should_track_json_source) {
1703 end_position = position();
1704 val_node = isolate_->factory()->NewSubString(
1705 source_, start_position, end_position);
1706 }
1707 break;
1708
1709 case JsonToken::NUMBER:
1710 value = ParseJsonNumber();
1711 if constexpr (should_track_json_source) {
1712 end_position = position();
1713 val_node = isolate_->factory()->NewSubString(
1714 source_, start_position, end_position);
1715 }
1716 break;
1717
1718 case JsonToken::LBRACE: {
1719 Consume(JsonToken::LBRACE);
1720 if (Check(JsonToken::RBRACE)) {
1721 // TODO(verwaest): Directly use the map instead.
1722 value = factory()->NewJSObject(object_constructor_);
1723 if constexpr (should_track_json_source) {
1724 val_node = ObjectTwoHashTable::New(isolate_, 0);
1725 }
1726 break;
1727 }
1728
1729 // Start parsing an object with properties.
1730 cont_stack.emplace_back(std::move(cont));
1731 cont = JsonContinuation(isolate_, JsonContinuation::kObjectProperty,
1732 property_stack_.size());
1733
1734 // Parse the property key.
1735 ExpectNext(JsonToken::STRING,
1736 MessageTemplate::kJsonParseExpectedPropNameOrRBrace);
1737 property_stack_.emplace_back(ScanJsonPropertyKey(&cont));
1738 if constexpr (should_track_json_source) {
1739 property_val_node_stack.emplace_back(Handle<Object>());
1740 }
1741
1742 ExpectNext(JsonToken::COLON,
1743 MessageTemplate::kJsonParseExpectedColonAfterPropertyName);
1744
1745 // Continue to start producing the first property value.
1746 continue;
1747 }
1748
1749 case JsonToken::LBRACK:
1750 Consume(JsonToken::LBRACK);
1751 if (Check(JsonToken::RBRACK)) {
1752 value = factory()->NewJSArray(0, PACKED_SMI_ELEMENTS);
1753 if constexpr (should_track_json_source) {
1754 val_node = factory()->NewFixedArray(0);
1755 }
1756 break;
1757 }
1758
1759 // Start parsing an array with elements.
1760 cont_stack.emplace_back(std::move(cont));
1761 cont = JsonContinuation(isolate_, JsonContinuation::kArrayElement,
1762 element_stack_.size());
1763
1764 // Continue to start producing the first array element.
1765 continue;
1766
1768 ScanLiteral("true");
1769 value = factory()->true_value();
1770 if constexpr (should_track_json_source) {
1771 val_node = isolate_->factory()->true_string();
1772 }
1773 break;
1774
1776 ScanLiteral("false");
1777 value = factory()->false_value();
1778 if constexpr (should_track_json_source) {
1779 val_node = isolate_->factory()->false_string();
1780 }
1781 break;
1782
1784 ScanLiteral("null");
1785 value = factory()->null_value();
1786 if constexpr (should_track_json_source) {
1787 val_node = isolate_->factory()->null_string();
1788 }
1789 break;
1790
1791 case JsonToken::COLON:
1792 case JsonToken::COMMA:
1793 case JsonToken::ILLEGAL:
1794 case JsonToken::RBRACE:
1795 case JsonToken::RBRACK:
1796 case JsonToken::EOS:
1797 ReportUnexpectedCharacter(CurrentCharacter());
1798 // Pop the continuation stack to correctly tear down handle scopes.
1799 while (!cont_stack.empty()) {
1800 cont = std::move(cont_stack.back());
1801 cont_stack.pop_back();
1802 }
1803 return MaybeHandle<Object>();
1804
1806 UNREACHABLE();
1807 }
1808 // Done producing a value, consume it.
1809 break;
1810 }
1811
1812 // Consume a produced json value.
1813 //
1814 // Iterate as long as values are produced (arrays or object literals are
1815 // finished).
1816 while (true) {
1817 // The switch is immediately followed by 'break' so we can use 'break' to
1818 // break out of the loop, and 'continue' to continue the loop.
1819 switch (cont.type()) {
1820 case JsonContinuation::kReturn:
1821 if constexpr (should_track_json_source) {
1822 DCHECK(!val_node.is_null());
1823 Tagged<Object> raw_value = *value;
1824 parsed_val_node_ = cont.scope.CloseAndEscape(val_node);
1825 return cont.scope.CloseAndEscape(handle(raw_value, isolate_));
1826 } else {
1827 return cont.scope.CloseAndEscape(value);
1828 }
1829
1830 case JsonContinuation::kObjectProperty: {
1831 // Store the previous property value into its property info.
1832 property_stack_.back().value = value;
1833 if constexpr (should_track_json_source) {
1834 property_val_node_stack.back() = val_node;
1835 }
1836
1837 if (V8_LIKELY(Check(JsonToken::COMMA))) {
1838 // Parse the property key.
1839 ExpectNext(
1841 MessageTemplate::kJsonParseExpectedDoubleQuotedPropertyName);
1842
1843 property_stack_.emplace_back(ScanJsonPropertyKey(&cont));
1844 if constexpr (should_track_json_source) {
1845 property_val_node_stack.emplace_back(Handle<Object>());
1846 }
1847 ExpectNext(
1849 MessageTemplate::kJsonParseExpectedColonAfterPropertyName);
1850
1851 // Break to start producing the subsequent property value.
1852 break;
1853 }
1854
1855 Handle<Map> feedback;
1856 if (cont_stack.size() > 0 &&
1857 cont_stack.back().type() == JsonContinuation::kArrayElement &&
1858 cont_stack.back().index < element_stack_.size() &&
1859 IsJSObject(*element_stack_.back())) {
1860 Tagged<Map> maybe_feedback =
1861 Cast<JSObject>(*element_stack_.back())->map();
1862 // Don't consume feedback from objects with a map that's detached
1863 // from the transition tree.
1864 if (!maybe_feedback->IsDetached(isolate_)) {
1865 feedback = handle(maybe_feedback, isolate_);
1866 }
1867 }
1868 value = BuildJsonObject<should_track_json_source>(cont, feedback);
1869 Expect(JsonToken::RBRACE,
1870 MessageTemplate::kJsonParseExpectedCommaOrRBrace);
1871 // Return the object.
1872 if constexpr (should_track_json_source) {
1873 size_t start = cont.index;
1874 int num_properties =
1875 static_cast<int>(property_stack_.size() - start);
1877 ObjectTwoHashTable::New(isolate(), num_properties);
1878 for (int i = 0; i < num_properties; i++) {
1879 const JsonProperty& property = property_stack_[start + i];
1880 DirectHandle<Object> property_val_node =
1881 property_val_node_stack[start + i];
1882 DirectHandle<Object> property_snapshot = property.value;
1884 if (property.string.is_index()) {
1885 key = factory()->Uint32ToString(property.string.index());
1886 } else {
1887 key = MakeString(property.string);
1888 }
1890 isolate(), table, key,
1891 {property_val_node, property_snapshot});
1892 }
1893 property_val_node_stack.resize(cont.index);
1895 Tagged<ObjectTwoHashTable> raw_table = *table;
1896 value = cont.scope.CloseAndEscape(value);
1897 val_node = cont.scope.CloseAndEscape(handle(raw_table, isolate_));
1898 } else {
1899 value = cont.scope.CloseAndEscape(value);
1900 }
1901 property_stack_.resize(cont.index);
1902
1903 // Pop the continuation.
1904 cont = std::move(cont_stack.back());
1905 cont_stack.pop_back();
1906 // Consume to produced object.
1907 continue;
1908 }
1909
1910 case JsonContinuation::kArrayElement: {
1911 // Store the previous element on the stack.
1912 element_stack_.emplace_back(value);
1913 if constexpr (should_track_json_source) {
1914 element_val_node_stack.emplace_back(val_node);
1915 }
1916 // Break to start producing the subsequent element value.
1917 if (V8_LIKELY(Check(JsonToken::COMMA))) break;
1918
1919 value = BuildJsonArray(cont.index);
1920 Expect(JsonToken::RBRACK,
1921 MessageTemplate::kJsonParseExpectedCommaOrRBrack);
1922 // Return the array.
1923 if constexpr (should_track_json_source) {
1924 size_t start = cont.index;
1925 int num_elements = static_cast<int>(element_stack_.size() - start);
1926 DirectHandle<FixedArray> val_node_and_snapshot_array =
1927 factory()->NewFixedArray(num_elements * 2);
1929 Tagged<FixedArray> raw_val_node_and_snapshot_array =
1930 *val_node_and_snapshot_array;
1931 for (int i = 0; i < num_elements; i++) {
1932 raw_val_node_and_snapshot_array->set(
1933 i * 2, *element_val_node_stack[start + i]);
1934 raw_val_node_and_snapshot_array->set(i * 2 + 1,
1935 *element_stack_[start + i]);
1936 }
1937 element_val_node_stack.resize(cont.index);
1938 value = cont.scope.CloseAndEscape(value);
1939 val_node = cont.scope.CloseAndEscape(
1940 handle(raw_val_node_and_snapshot_array, isolate_));
1941 } else {
1942 value = cont.scope.CloseAndEscape(value);
1943 }
1944 element_stack_.resize(cont.index);
1945 // Pop the continuation.
1946 cont = std::move(cont_stack.back());
1947 cont_stack.pop_back();
1948 // Consume the produced array.
1949 continue;
1950 }
1951 }
1952
1953 // Done consuming a value. Produce next value.
1954 break;
1955 }
1956 }
1957}
1958
1959template <typename Char>
1961 cursor_ =
1962 std::find_if(cursor_, end_, [](Char c) { return !IsDecimalDigit(c); });
1963}
1964
1965template <typename Char>
1967 double double_number;
1968 int smi_number;
1969 if (ParseJsonNumberAsDoubleOrSmi(&double_number, &smi_number)) {
1970 return factory()->NewHeapNumber(double_number);
1971 }
1972 return handle(Smi::FromInt(smi_number), isolate_);
1973}
1974
1975template <typename Char>
1977 int* result_smi) {
1978 int sign = 1;
1979
1980 {
1981 const Char* start = cursor_;
1983
1984 base::uc32 c = *cursor_;
1985 if (c == '-') {
1986 sign = -1;
1987 c = NextCharacter();
1988 }
1989
1990 if (c == '0') {
1991 // Prefix zero is only allowed if it's the only digit before
1992 // a decimal point or exponent.
1993 c = NextCharacter();
1994 if (base::IsInRange(c, 0,
1995 static_cast<int32_t>(unibrow::Latin1::kMaxChar)) &&
1996 IsNumberPart(character_json_scan_flags[c])) {
1997 if (V8_UNLIKELY(IsDecimalDigit(c))) {
1998 AllowGarbageCollection allow_before_exception;
1999 ReportUnexpectedToken(JsonToken::NUMBER);
2000 *result_smi = 0;
2001 return false;
2002 }
2003 } else if (sign > 0) {
2004 *result_smi = 0;
2005 return false;
2006 }
2007 } else {
2008 const Char* smi_start = cursor_;
2009 static_assert(Smi::IsValid(-999999999));
2010 static_assert(Smi::IsValid(999999999));
2011 const int kMaxSmiLength = 9;
2012 int32_t i = 0;
2013 const Char* stop = cursor_ + kMaxSmiLength;
2014 if (stop > end_) stop = end_;
2015 while (cursor_ < stop && IsDecimalDigit(*cursor_)) {
2016 i = (i * 10) + ((*cursor_) - '0');
2017 cursor_++;
2018 }
2019 if (V8_UNLIKELY(smi_start == cursor_)) {
2020 AllowGarbageCollection allow_before_exception;
2021 ReportUnexpectedToken(
2023 MessageTemplate::kJsonParseNoNumberAfterMinusSign);
2024 *result_smi = 0;
2025 return false;
2026 }
2027 c = CurrentCharacter();
2028 if (!base::IsInRange(c, 0,
2029 static_cast<int32_t>(unibrow::Latin1::kMaxChar)) ||
2030 !IsNumberPart(character_json_scan_flags[c])) {
2031 // Smi.
2032 // TODO(verwaest): Cache?
2033 *result_smi = i * sign;
2034 return false;
2035 }
2036 AdvanceToNonDecimal();
2037 }
2038
2039 if (CurrentCharacter() == '.') {
2040 c = NextCharacter();
2041 if (!IsDecimalDigit(c)) {
2042 AllowGarbageCollection allow_before_exception;
2043 ReportUnexpectedToken(
2045 MessageTemplate::kJsonParseUnterminatedFractionalNumber);
2046 *result_smi = 0;
2047 return false;
2048 }
2049 AdvanceToNonDecimal();
2050 }
2051
2052 if (AsciiAlphaToLower(CurrentCharacter()) == 'e') {
2053 c = NextCharacter();
2054 if (c == '-' || c == '+') c = NextCharacter();
2055 if (!IsDecimalDigit(c)) {
2056 AllowGarbageCollection allow_before_exception;
2057 ReportUnexpectedToken(
2059 MessageTemplate::kJsonParseExponentPartMissingNumber);
2060 *result_smi = 0;
2061 return false;
2062 }
2063 AdvanceToNonDecimal();
2064 }
2065
2067 *result_double =
2068 StringToDouble(chars,
2069 NO_CONVERSION_FLAG, // Hex, octal or trailing junk.
2070 std::numeric_limits<double>::quiet_NaN());
2071 DCHECK(!std::isnan(*result_double));
2072
2073 // The result might still be a smi even if it has a decimal part.
2074 return !DoubleToSmiInteger(*result_double, result_smi);
2075 }
2076}
2077
2078namespace {
2079
2080template <typename Char>
2081bool Matches(base::Vector<const Char> chars, DirectHandle<String> string) {
2082 DCHECK(!string.is_null());
2083 return string->IsEqualTo(chars);
2084}
2085
2086} // namespace
2087
2088template <typename Char>
2089template <typename SinkSeqString>
2091 const JsonString& string, Handle<SinkSeqString> intermediate,
2092 Handle<String> hint) {
2093 using SinkChar = typename SinkSeqString::Char;
2094 {
2096 SinkChar* dest = intermediate->GetChars(no_gc);
2097 if (!string.has_escape()) {
2098 DCHECK(!string.internalize());
2099 CopyChars(dest, chars_ + string.start(), string.length());
2100 return intermediate;
2101 }
2102 DecodeString(dest, string.start(), string.length());
2103
2104 if (!string.internalize()) return intermediate;
2105
2107 if (!hint.is_null() && Matches(data, hint)) return hint;
2108 }
2109
2110 DCHECK_EQ(intermediate->length(), string.length());
2111 return factory()->InternalizeString(intermediate);
2112}
2113
2114template <typename Char>
2116 Handle<String> hint) {
2117 if (string.length() == 0) return factory()->empty_string();
2118 if (string.length() == 1) {
2119 uint16_t first_char;
2120 if (!string.has_escape()) {
2121 first_char = chars_[string.start()];
2122 } else {
2123 DecodeString(&first_char, string.start(), 1);
2124 }
2125 return factory()->LookupSingleCharacterStringFromCode(first_char);
2126 }
2127
2128 if (string.internalize() && !string.has_escape()) {
2129 if (!hint.is_null()) {
2130 base::Vector<const Char> data(chars_ + string.start(), string.length());
2131 if (Matches(data, hint)) return hint;
2132 }
2133 if (chars_may_relocate_) {
2134 return factory()->InternalizeSubString(Cast<SeqString>(source_),
2135 string.start(), string.length(),
2136 string.needs_conversion());
2137 }
2138 base::Vector<const Char> chars(chars_ + string.start(), string.length());
2139 return factory()->InternalizeString(chars, string.needs_conversion());
2140 }
2141
2142 if (sizeof(Char) == 1 ? V8_LIKELY(!string.needs_conversion())
2143 : string.needs_conversion()) {
2144 Handle<SeqOneByteString> intermediate =
2145 factory()->NewRawOneByteString(string.length()).ToHandleChecked();
2146 return DecodeString(string, intermediate, hint);
2147 }
2148
2149 Handle<SeqTwoByteString> intermediate =
2150 factory()->NewRawTwoByteString(string.length()).ToHandleChecked();
2151 return DecodeString(string, intermediate, hint);
2152}
2153
2154template <typename Char>
2155template <typename SinkChar>
2156void JsonParser<Char>::DecodeString(SinkChar* sink, uint32_t start,
2157 uint32_t length) {
2158 SinkChar* sink_start = sink;
2159 const Char* cursor = chars_ + start;
2160 while (true) {
2161 const Char* end = cursor + length - (sink - sink_start);
2162 cursor = std::find_if(cursor, end, [&sink](Char c) {
2163 if (c == '\\') return true;
2164 *sink++ = c;
2165 return false;
2166 });
2167
2168 if (cursor == end) return;
2169
2170 cursor++;
2171
2172 switch (GetEscapeKind(character_json_scan_flags[*cursor])) {
2173 case EscapeKind::kSelf:
2174 *sink++ = *cursor;
2175 break;
2176
2177 case EscapeKind::kBackspace:
2178 *sink++ = '\x08';
2179 break;
2180
2181 case EscapeKind::kTab:
2182 *sink++ = '\x09';
2183 break;
2184
2185 case EscapeKind::kNewLine:
2186 *sink++ = '\x0A';
2187 break;
2188
2189 case EscapeKind::kFormFeed:
2190 *sink++ = '\x0C';
2191 break;
2192
2193 case EscapeKind::kCarriageReturn:
2194 *sink++ = '\x0D';
2195 break;
2196
2197 case EscapeKind::kUnicode: {
2198 base::uc32 value = 0;
2199 for (int i = 0; i < 4; i++) {
2200 value = value * 16 + base::HexValue(*++cursor);
2201 }
2202 if (value <=
2204 *sink++ = value;
2205 } else {
2206 *sink++ = unibrow::Utf16::LeadSurrogate(value);
2207 *sink++ = unibrow::Utf16::TrailSurrogate(value);
2208 }
2209 break;
2210 }
2211
2212 case EscapeKind::kIllegal:
2213 UNREACHABLE();
2214 }
2215 cursor++;
2216 }
2217}
2218
2219template <typename Char>
2222 uint32_t start = position();
2223 uint32_t offset = start;
2224 bool has_escape = false;
2225 base::uc32 bits = 0;
2226
2227 while (true) {
2228 cursor_ = std::find_if(cursor_, end_, [&bits](Char c) {
2229 if (sizeof(Char) == 2 && V8_UNLIKELY(c > unibrow::Latin1::kMaxChar)) {
2230 bits |= c;
2231 return false;
2232 }
2233 return MayTerminateJsonString(character_json_scan_flags[c]);
2234 });
2235
2236 if (V8_UNLIKELY(is_at_end())) {
2237 AllowGarbageCollection allow_before_exception;
2238 ReportUnexpectedToken(JsonToken::ILLEGAL,
2239 MessageTemplate::kJsonParseUnterminatedString);
2240 break;
2241 }
2242
2243 if (*cursor_ == '"') {
2244 uint32_t end = position();
2245 advance();
2246 uint32_t length = end - offset;
2247 bool convert = sizeof(Char) == 1 ? bits > unibrow::Latin1::kMaxChar
2248 : bits <= unibrow::Latin1::kMaxChar;
2249 constexpr int kMaxInternalizedStringValueLength = 10;
2250 bool internalize =
2251 needs_internalization ||
2252 (sizeof(Char) == 1 && length < kMaxInternalizedStringValueLength);
2253 return JsonString(start, length, convert, internalize, has_escape);
2254 }
2255
2256 if (*cursor_ == '\\') {
2257 has_escape = true;
2258 base::uc32 c = NextCharacter();
2260 c, 0, static_cast<int32_t>(unibrow::Latin1::kMaxChar)))) {
2261 AllowGarbageCollection allow_before_exception;
2262 ReportUnexpectedCharacter(c);
2263 break;
2264 }
2265
2266 switch (GetEscapeKind(character_json_scan_flags[c])) {
2267 case EscapeKind::kSelf:
2268 case EscapeKind::kBackspace:
2269 case EscapeKind::kTab:
2270 case EscapeKind::kNewLine:
2271 case EscapeKind::kFormFeed:
2272 case EscapeKind::kCarriageReturn:
2273 offset += 1;
2274 break;
2275
2276 case EscapeKind::kUnicode: {
2277 base::uc32 value = ScanUnicodeCharacter();
2278 if (value == kInvalidUnicodeCharacter) {
2279 AllowGarbageCollection allow_before_exception;
2280 ReportUnexpectedToken(JsonToken::ILLEGAL,
2281 MessageTemplate::kJsonParseBadUnicodeEscape);
2282 return JsonString();
2283 }
2284 bits |= value;
2285 // \uXXXX results in either 1 or 2 Utf16 characters, depending on
2286 // whether the decoded value requires a surrogate pair.
2287 offset += 5 - (value > static_cast<base::uc32>(
2289 break;
2290 }
2291
2292 case EscapeKind::kIllegal:
2293 AllowGarbageCollection allow_before_exception;
2294 ReportUnexpectedToken(JsonToken::ILLEGAL,
2295 MessageTemplate::kJsonParseBadEscapedCharacter);
2296 return JsonString();
2297 }
2298
2299 advance();
2300 continue;
2301 }
2302
2303 DCHECK_LT(*cursor_, 0x20);
2304 AllowGarbageCollection allow_before_exception;
2305 ReportUnexpectedToken(JsonToken::ILLEGAL,
2306 MessageTemplate::kJsonParseBadControlCharacter);
2307 break;
2308 }
2309
2310 return JsonString();
2311}
2312
2313// Explicit instantiation.
2314template class JsonParser<uint8_t>;
2315template class JsonParser<uint16_t>;
2316
2317} // namespace internal
2318} // namespace v8
Isolate * isolate_
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
Builtins::Kind kind
Definition builtins.cc:40
SourcePosition pos
static const uint16_t kMaxChar
Definition unicode.h:142
static uint16_t LeadSurrogate(uint32_t char_code)
Definition unicode.h:126
static const uchar kMaxNonSurrogateCharCode
Definition unicode.h:116
static uint16_t TrailSurrogate(uint32_t char_code)
Definition unicode.h:129
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
void emplace_back(Args &&... args)
void resize(size_t new_size)
void OnCompileError(DirectHandle< Script > script)
Definition debug.cc:2757
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
V8_WARN_UNUSED_RESULT Handle< String > NumberToString(DirectHandle< Object > number, NumberCacheMode mode=NumberCacheMode::kBoth)
Handle< String > LookupSingleCharacterStringFromCode(uint16_t code)
Handle< Script > NewScript(DirectHandle< UnionOf< String, Undefined > > source, ScriptEventType event_type=ScriptEventType::kCreate)
Handle< Number > NewNumber(double value)
Handle< JSObject > NewJSObject(DirectHandle< JSFunction > constructor, AllocationType allocation=AllocationType::kYoung, NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:2985
Handle< Map > ObjectLiteralMapFromCache(DirectHandle< NativeContext > native_context, int number_of_properties)
Definition factory.cc:4083
Handle< JSObject > NewJSObjectFromMap(DirectHandle< Map > map, AllocationType allocation=AllocationType::kYoung, DirectHandle< AllocationSite > allocation_site=DirectHandle< AllocationSite >::null(), NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:3135
Handle< JSObject > NewSlowJSObjectFromMap(DirectHandle< Map > map, int number_of_slow_properties, AllocationType allocation=AllocationType::kYoung, DirectHandle< AllocationSite > allocation_site=DirectHandle< AllocationSite >::null(), NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:3165
HandleType< String > NewSubString(HandleType< T > str, uint32_t begin, uint32_t end)
Definition factory-inl.h:88
static FieldIndex ForPropertyIndex(Tagged< Map > map, int index, Representation representation=Representation::Tagged())
static FieldIndex ForInObjectOffset(int offset, Encoding encoding)
static V8_EXPORT_PRIVATE Tagged< FieldType > Any()
Definition field-type.cc:22
static bool NowContains(Tagged< FieldType > type, Tagged< Object > value)
FoldedMutableHeapNumberAllocation(Isolate *isolate, int count)
FoldedMutableHeapNumberAllocator(Isolate *isolate, FoldedMutableHeapNumberAllocation *allocation, DisallowGarbageCollection &no_gc)
Tagged< HeapNumber > AllocateNext(ReadOnlyRoots roots, Float64 value)
Handle< Object > script() const
V8_INLINE bool is_null() const
Definition handles.h:69
HandleType< T > CloseAndEscape(HandleType< T > handle_value)
static Tagged< HeapObject > FromAddress(Address address)
void EnsureSweepingCompletedForObject(Tagged< HeapObject > object)
Definition heap.cc:2417
Tagged< Object > Throw(Tagged< Object > exception, MessageLocation *location=nullptr)
Definition isolate.cc:2091
Handle< NativeContext > native_context()
Definition isolate-inl.h:48
Tagged< Object > ThrowAt(DirectHandle< JSObject > exception, MessageLocation *location)
Definition isolate.cc:1925
v8::internal::Factory * factory()
Definition isolate.h:1527
Debug * debug() const
Definition isolate.h:1474
LocalHeap * main_thread_local_heap()
Definition isolate.cc:7479
bool NeedsSourcePositions() const
Definition isolate.cc:6155
Handle< JSObject > BuildFromIterator(PropertyIterator &&it, MaybeHandle< FixedArrayBase > maybe_elements={})
JSDataObjectBuilder(Isolate *isolate, ElementsKind elements_kind, int expected_named_properties, DirectHandle< Map > expected_final_map, HeapNumberMode heap_number_mode)
V8_INLINE void RegisterFieldNeedsFreshHeapNumber(DirectHandle< Object > value)
V8_INLINE void CreateAndInitialiseObject(ValueIterator value_it, DirectHandle< FixedArrayBase > elements)
V8_INLINE bool TryFastTransitionToPropertyKey(base::Vector< const Char > key_chars, GetKeyFunction &&get_key, Handle< String > *key_out)
V8_INLINE bool TryGeneralizeFieldToValue(DirectHandle< Object > value)
DirectHandle< Map > expected_final_map_
V8_INLINE bool TryAddFastPropertyForValue(base::Vector< const Char > key_chars, GetKeyFunction &&get_key, GetValueFunction &&get_value)
void AddSlowProperty(DirectHandle< String > key, Handle< Object > value)
V8_INLINE bool IsOnExpectedFinalMapFastPath() const
static V8_WARN_UNUSED_RESULT HandleType< Object >::MaybeType DefineOwnPropertyIgnoreAttributes(LookupIterator *it, HandleType< T > value, PropertyAttributes attributes, AccessorInfoHandling handling=DONT_FORCE_FIELD, EnforceDefineSemantics semantics=EnforceDefineSemantics::kSet)
static V8_EXPORT_PRIVATE void AddProperty(Isolate *isolate, DirectHandle< JSObject > object, DirectHandle< Name > name, DirectHandle< Object > value, PropertyAttributes attributes)
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT Maybe< bool > DeletePropertyOrElement(Isolate *isolate, DirectHandle< JSReceiver > object, DirectHandle< Name > name, LanguageMode language_mode=LanguageMode::kSloppy)
static V8_WARN_UNUSED_RESULT Maybe< bool > CreateDataProperty(Isolate *isolate, DirectHandle< JSReceiver > object, DirectHandle< Name > key, DirectHandle< Object > value, Maybe< ShouldThrow > should_throw)
static V8_WARN_UNUSED_RESULT Maybe< bool > DefineOwnProperty(Isolate *isolate, DirectHandle< JSReceiver > object, DirectHandle< Object > key, PropertyDescriptor *desc, Maybe< ShouldThrow > should_throw)
MaybeHandle< Object > InternalizeJsonProperty(DirectHandle< JSReceiver > holder, DirectHandle< String > key, Handle< Object > val_node, DirectHandle< Object > snapshot)
static MaybeHandle< Object > Internalize(Isolate *isolate, DirectHandle< Object > result, Handle< Object > reviver, Handle< String > source, MaybeHandle< Object > val_node)
bool RecurseAndApply(Handle< JSReceiver > holder, Handle< String > name, Handle< Object > val_node, Handle< Object > snapshot)
Handle< Object > GetValue(bool will_revisit_value)
Handle< String > GetKey(Handle< String > expected_key_hint)
NamedPropertyIterator(JsonParser< Char > &parser, const JsonProperty *it, const JsonProperty *end)
JsonString ScanJsonString(bool needs_internalization)
MaybeHandle< Object > ParseJsonArray()
Handle< String > MakeString(const JsonString &string, Handle< String > hint=Handle< String >())
MaybeHandle< Object > ParseJsonValue()
MessageTemplate GetErrorMessageWithEllipses(DirectHandle< Object > &arg, DirectHandle< Object > &arg2, int pos)
void ReportUnexpectedToken(JsonToken token, std::optional< MessageTemplate > errorMessage=std::nullopt)
MaybeHandle< Object > ParseJsonObject(Handle< Map > feedback)
JsonParser(Isolate *isolate, Handle< String > source)
void DecodeString(SinkChar *sink, uint32_t start, uint32_t length)
JsonString ScanJsonPropertyKey(JsonContinuation *cont)
static void UpdatePointersCallback(void *parser)
Handle< JSObject > BuildJsonObject(const JsonContinuation &cont, DirectHandle< Map > feedback)
Handle< String > source_
V8_INLINE MaybeHandle< Object > ParseJsonValueRecursive(Handle< Map > feedback={})
void CalculateFileLocation(DirectHandle< Object > &line, DirectHandle< Object > &column)
Handle< Object > ParseJsonNumber()
base::uc32 ScanUnicodeCharacter()
MaybeHandle< Object > ParseJson(DirectHandle< Object > reviver)
void ReportUnexpectedCharacter(base::uc32 c)
bool ParseJsonNumberAsDoubleOrSmi(double *result_double, int *result_smi)
Handle< Object > BuildJsonArray(size_t start)
MessageTemplate LookUpErrorMessageForJsonToken(JsonToken token, DirectHandle< Object > &arg, DirectHandle< Object > &arg2, int pos)
uint32_t index() const
Definition json-parser.h:77
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)
static void GeneralizeField(Isolate *isolate, DirectHandle< Map > map, InternalIndex modify_index, PropertyConstness new_constness, Representation new_representation, DirectHandle< FieldType > new_field_type)
static V8_EXPORT_PRIVATE DirectHandle< Map > Update(Isolate *isolate, DirectHandle< Map > map)
Definition map.cc:839
static V8_EXPORT_PRIVATE Handle< Map > AsElementsKind(Isolate *isolate, DirectHandle< Map > map, ElementsKind kind)
Definition map.cc:1163
V8_EXPORT_PRIVATE static V8_WARN_UNUSED_RESULT MaybeHandle< Map > CopyWithField(Isolate *isolate, DirectHandle< Map > map, DirectHandle< Name > name, DirectHandle< FieldType > type, PropertyAttributes attributes, PropertyConstness constness, Representation representation, TransitionFlag flag)
Definition map.cc:504
V8_INLINE Handle< T > ToHandleChecked() const
V8_WARN_UNUSED_RESULT V8_INLINE bool ToHandle(Handle< S > *out) const
NamedPropertyValueIterator(const JsonProperty *it, const JsonProperty *end)
NamedPropertyValueIterator & operator++()
bool operator!=(const NamedPropertyValueIterator &other) const
static void UncheckedSet(Isolate *isolate, DirectHandle< NumberDictionary > dictionary, uint32_t key, DirectHandle< Object > value)
Definition objects.cc:5907
static Handle< ObjectTwoHashTable > Put(Isolate *isolate, Handle< ObjectTwoHashTable > table, DirectHandle< Object > key, const std::array< DirectHandle< Object >, N > &values)
Definition objects.cc:6224
static V8_WARN_UNUSED_RESULT MaybeHandle< Object > GetPropertyOrElement(Isolate *isolate, DirectHandle< JSAny > object, DirectHandle< Name > name)
static DirectHandle< FieldType > OptimalType(Tagged< Object > obj, Isolate *isolate, Representation representation)
Definition objects.cc:224
static Representation OptimalRepresentation(Tagged< Object > obj, PtrComprCageBase cage_base)
static double NumberValue(Tagged< Number > obj)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< Object > GetLengthFromArrayLike(Isolate *isolate, DirectHandle< JSReceiver > object)
Definition objects.cc:1238
static V8_EXPORT_PRIVATE bool SameValue(Tagged< Object > obj, Tagged< Object > other)
Definition objects.cc:1706
static bool FitsRepresentation(Tagged< Object > obj, Representation representation, bool allow_coercion=true)
void set_value(DirectHandle< JSAny > value)
PropertyAttributes attributes() const
PropertyLocation location() const
Representation representation() const
PropertyConstness constness() const
constexpr bool IsHeapObject() const
Representation generalize(Representation other)
constexpr bool IsDouble() const
bool CanBeInPlaceChangedTo(const Representation &other) const
static constexpr Tagged< Smi > FromInt(int value)
Definition smi.h:38
static bool constexpr IsValid(T value)
Definition smi.h:67
V8_INLINE bool IsExternal() const
Definition string-inl.h:188
static V8_INLINE HandleType< String > Flatten(Isolate *isolate, HandleType< T > string, AllocationType allocation=AllocationType::kYoung)
static bool CanHaveMoreTransitions(Isolate *isolate, DirectHandle< Map > map)
MaybeHandle< Map > FindTransitionToField(DirectHandle< String > name)
uint8_t *const start_
Definition assembler.cc:131
#define USE_ALLOCATION_ALIGNMENT_BOOL
Definition globals.h:1562
#define ALIGN_TO_ALLOCATION_ALIGNMENT(value)
Definition globals.h:1796
Handle< String > source_
Definition compiler.cc:3791
const v8::base::TimeTicks end_
Definition sweeper.cc:54
int start
int end
LineAndColumn current
#define ASSIGN_RETURN_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:291
#define ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, dst, call, value)
Definition isolate.h:276
#define STACK_CHECK(isolate, result_value)
Definition isolate.h:3067
#define MAYBE_RETURN(call, value)
Definition isolate.h:408
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
Isolate * isolate
int32_t offset
#define CASES(V)
#define CALL_GET_SCAN_FLAGS(N)
ZoneVector< RpoNumber > & result
int position
Definition liveedit.cc:290
BitField< T, shift, size, uint8_t > BitField8
Definition bit-field.h:90
uint32_t uc32
Definition strings.h:19
int HexValue(uc32 c)
Definition strings.h:34
constexpr bool IsInRange(T value, U lower_limit, U higher_limit)
Definition bounds.h:20
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
FloatWithBits< 64 > Float64
Definition index.h:234
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
constexpr int kTaggedSize
Definition globals.h:542
@ SKIP_WRITE_BARRIER
Definition objects.h:52
bool TryAddArrayIndexChar(uint32_t *index, Char c)
Definition utils-inl.h:38
bool DoubleToSmiInteger(double value, int *smi_int_value)
@ INSERT_TRANSITION
Definition objects.h:66
V8_INLINE constexpr bool IsSmi(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:665
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
Definition flags.cc:2086
MaybeDirectHandle< Object > InternalizeJsonProperty(Handle< JSObject > holder, Handle< String > key)
DONT_OVERRIDE DISABLE_ALLOCATION_SITES HOLEY_ELEMENTS
void CopyChars(DstType *dst, const SrcType *src, size_t count) V8_NONNULL(1
constexpr bool IsDecimalDigit(base::uc32 c)
V8_INLINE constexpr bool IsHeapObject(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:669
static bool ShouldConvertToSlowElements(uint32_t used_elements, uint32_t new_capacity)
return value
Definition map-inl.h:893
constexpr int AsciiAlphaToLower(base::uc32 c)
uint64_t HashSeed(Isolate *isolate)
double StringToDouble(const char *str, ConversionFlag flags, double empty_string_val)
kInterpreterTrampolineOffset script
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Maybe< T > Nothing()
Definition v8-maybe.h:112
Maybe< T > Just(const T &t)
Definition v8-maybe.h:117
RegExpParserImpl< CharT > *const parser_
base::Vector< const char > contents
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#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
#define INT_0_TO_127_LIST(V)
Definition utils.h:625
#define V8_INLINE
Definition v8config.h:500
#define V8_LIKELY(condition)
Definition v8config.h:661
#define V8_UNLIKELY(condition)
Definition v8config.h:660