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
5#include "src/json-stringifier.h"
6
7#include "src/conversions.h"
8#include "src/lookup.h"
9#include "src/message-template.h"
10#include "src/objects-inl.h"
11#include "src/objects/heap-number-inl.h"
12#include "src/objects/js-array-inl.h"
13#include "src/objects/oddball-inl.h"
14#include "src/objects/ordered-hash-table.h"
15#include "src/objects/smi.h"
16#include "src/string-builder-inl.h"
17#include "src/utils.h"
18
19namespace v8 {
20namespace internal {
21
22class JsonStringifier {
23 public:
24 explicit JsonStringifier(Isolate* isolate);
25
26 ~JsonStringifier() { DeleteArray(gap_); }
27
28 V8_WARN_UNUSED_RESULT MaybeHandle<Object> Stringify(Handle<Object> object,
29 Handle<Object> replacer,
30 Handle<Object> gap);
31
32 private:
33 enum Result { UNCHANGED, SUCCESS, EXCEPTION };
34
35 bool InitializeReplacer(Handle<Object> replacer);
36 bool InitializeGap(Handle<Object> gap);
37
38 V8_WARN_UNUSED_RESULT MaybeHandle<Object> ApplyToJsonFunction(
39 Handle<Object> object, Handle<Object> key);
40 V8_WARN_UNUSED_RESULT MaybeHandle<Object> ApplyReplacerFunction(
41 Handle<Object> value, Handle<Object> key, Handle<Object> initial_holder);
42
43 // Entry point to serialize the object.
44 V8_INLINE Result SerializeObject(Handle<Object> obj) {
45 return Serialize_<false>(obj, false, factory()->empty_string());
46 }
47
48 // Serialize an array element.
49 // The index may serve as argument for the toJSON function.
50 V8_INLINE Result SerializeElement(Isolate* isolate, Handle<Object> object,
51 int i) {
52 return Serialize_<false>(object, false,
53 Handle<Object>(Smi::FromInt(i), isolate));
54 }
55
56 // Serialize a object property.
57 // The key may or may not be serialized depending on the property.
58 // The key may also serve as argument for the toJSON function.
59 V8_INLINE Result SerializeProperty(Handle<Object> object, bool deferred_comma,
60 Handle<String> deferred_key) {
61 DCHECK(!deferred_key.is_null());
62 return Serialize_<true>(object, deferred_comma, deferred_key);
63 }
64
65 template <bool deferred_string_key>
66 Result Serialize_(Handle<Object> object, bool comma, Handle<Object> key);
67
68 V8_INLINE void SerializeDeferredKey(bool deferred_comma,
69 Handle<Object> deferred_key);
70
71 Result SerializeSmi(Smi object);
72
73 Result SerializeDouble(double number);
74 V8_INLINE Result SerializeHeapNumber(Handle<HeapNumber> object) {
75 return SerializeDouble(object->value());
76 }
77
78 Result SerializeJSValue(Handle<JSValue> object, Handle<Object> key);
79
80 V8_INLINE Result SerializeJSArray(Handle<JSArray> object, Handle<Object> key);
81 V8_INLINE Result SerializeJSObject(Handle<JSObject> object,
82 Handle<Object> key);
83
84 Result SerializeJSProxy(Handle<JSProxy> object, Handle<Object> key);
85 Result SerializeJSReceiverSlow(Handle<JSReceiver> object);
86 Result SerializeArrayLikeSlow(Handle<JSReceiver> object, uint32_t start,
87 uint32_t length);
88
89 void SerializeString(Handle<String> object);
90
91 template <typename SrcChar, typename DestChar>
92 V8_INLINE static void SerializeStringUnchecked_(
93 Vector<const SrcChar> src,
94 IncrementalStringBuilder::NoExtend<DestChar>* dest);
95
96 template <typename SrcChar, typename DestChar>
97 V8_INLINE void SerializeString_(Handle<String> string);
98
99 template <typename Char>
100 V8_INLINE static bool DoNotEscape(Char c);
101
102 V8_INLINE void NewLine();
103 V8_INLINE void Indent() { indent_++; }
104 V8_INLINE void Unindent() { indent_--; }
105 V8_INLINE void Separator(bool first);
106
107 Handle<JSReceiver> CurrentHolder(Handle<Object> value,
108 Handle<Object> inital_holder);
109
110 Result StackPush(Handle<Object> object, Handle<Object> key);
111 void StackPop();
112
113 // Uses the current stack_ to provide a detailed error message of
114 // the objects involved in the circular structure.
115 Handle<String> ConstructCircularStructureErrorMessage(Handle<Object> last_key,
116 size_t start_index);
117 // The prefix and postfix count do NOT include the starting and
118 // closing lines of the error message.
119 static const int kCircularErrorMessagePrefixCount = 2;
120 static const int kCircularErrorMessagePostfixCount = 1;
121
122 Factory* factory() { return isolate_->factory(); }
123
124 Isolate* isolate_;
125 IncrementalStringBuilder builder_;
126 Handle<String> tojson_string_;
127 Handle<FixedArray> property_list_;
128 Handle<JSReceiver> replacer_function_;
129 uc16* gap_;
130 int indent_;
131
132 using KeyObject = std::pair<Handle<Object>, Handle<Object>>;
133 std::vector<KeyObject> stack_;
134
135 static const int kJsonEscapeTableEntrySize = 8;
136 static const char* const JsonEscapeTable;
137};
138
139MaybeHandle<Object> JsonStringify(Isolate* isolate, Handle<Object> object,
140 Handle<Object> replacer, Handle<Object> gap) {
141 JsonStringifier stringifier(isolate);
142 return stringifier.Stringify(object, replacer, gap);
143}
144
145// Translation table to escape Latin1 characters.
146// Table entries start at a multiple of 8 and are null-terminated.
147const char* const JsonStringifier::JsonEscapeTable =
148 "\\u0000\0 \\u0001\0 \\u0002\0 \\u0003\0 "
149 "\\u0004\0 \\u0005\0 \\u0006\0 \\u0007\0 "
150 "\\b\0 \\t\0 \\n\0 \\u000b\0 "
151 "\\f\0 \\r\0 \\u000e\0 \\u000f\0 "
152 "\\u0010\0 \\u0011\0 \\u0012\0 \\u0013\0 "
153 "\\u0014\0 \\u0015\0 \\u0016\0 \\u0017\0 "
154 "\\u0018\0 \\u0019\0 \\u001a\0 \\u001b\0 "
155 "\\u001c\0 \\u001d\0 \\u001e\0 \\u001f\0 "
156 " \0 !\0 \\\"\0 #\0 "
157 "$\0 %\0 &\0 '\0 "
158 "(\0 )\0 *\0 +\0 "
159 ",\0 -\0 .\0 /\0 "
160 "0\0 1\0 2\0 3\0 "
161 "4\0 5\0 6\0 7\0 "
162 "8\0 9\0 :\0 ;\0 "
163 "<\0 =\0 >\0 ?\0 "
164 "@\0 A\0 B\0 C\0 "
165 "D\0 E\0 F\0 G\0 "
166 "H\0 I\0 J\0 K\0 "
167 "L\0 M\0 N\0 O\0 "
168 "P\0 Q\0 R\0 S\0 "
169 "T\0 U\0 V\0 W\0 "
170 "X\0 Y\0 Z\0 [\0 "
171 "\\\\\0 ]\0 ^\0 _\0 "
172 "`\0 a\0 b\0 c\0 "
173 "d\0 e\0 f\0 g\0 "
174 "h\0 i\0 j\0 k\0 "
175 "l\0 m\0 n\0 o\0 "
176 "p\0 q\0 r\0 s\0 "
177 "t\0 u\0 v\0 w\0 "
178 "x\0 y\0 z\0 {\0 "
179 "|\0 }\0 ~\0 \x7F\0 "
180 "\x80\0 \x81\0 \x82\0 \x83\0 "
181 "\x84\0 \x85\0 \x86\0 \x87\0 "
182 "\x88\0 \x89\0 \x8A\0 \x8B\0 "
183 "\x8C\0 \x8D\0 \x8E\0 \x8F\0 "
184 "\x90\0 \x91\0 \x92\0 \x93\0 "
185 "\x94\0 \x95\0 \x96\0 \x97\0 "
186 "\x98\0 \x99\0 \x9A\0 \x9B\0 "
187 "\x9C\0 \x9D\0 \x9E\0 \x9F\0 "
188 "\xA0\0 \xA1\0 \xA2\0 \xA3\0 "
189 "\xA4\0 \xA5\0 \xA6\0 \xA7\0 "
190 "\xA8\0 \xA9\0 \xAA\0 \xAB\0 "
191 "\xAC\0 \xAD\0 \xAE\0 \xAF\0 "
192 "\xB0\0 \xB1\0 \xB2\0 \xB3\0 "
193 "\xB4\0 \xB5\0 \xB6\0 \xB7\0 "
194 "\xB8\0 \xB9\0 \xBA\0 \xBB\0 "
195 "\xBC\0 \xBD\0 \xBE\0 \xBF\0 "
196 "\xC0\0 \xC1\0 \xC2\0 \xC3\0 "
197 "\xC4\0 \xC5\0 \xC6\0 \xC7\0 "
198 "\xC8\0 \xC9\0 \xCA\0 \xCB\0 "
199 "\xCC\0 \xCD\0 \xCE\0 \xCF\0 "
200 "\xD0\0 \xD1\0 \xD2\0 \xD3\0 "
201 "\xD4\0 \xD5\0 \xD6\0 \xD7\0 "
202 "\xD8\0 \xD9\0 \xDA\0 \xDB\0 "
203 "\xDC\0 \xDD\0 \xDE\0 \xDF\0 "
204 "\xE0\0 \xE1\0 \xE2\0 \xE3\0 "
205 "\xE4\0 \xE5\0 \xE6\0 \xE7\0 "
206 "\xE8\0 \xE9\0 \xEA\0 \xEB\0 "
207 "\xEC\0 \xED\0 \xEE\0 \xEF\0 "
208 "\xF0\0 \xF1\0 \xF2\0 \xF3\0 "
209 "\xF4\0 \xF5\0 \xF6\0 \xF7\0 "
210 "\xF8\0 \xF9\0 \xFA\0 \xFB\0 "
211 "\xFC\0 \xFD\0 \xFE\0 \xFF\0 ";
212
213JsonStringifier::JsonStringifier(Isolate* isolate)
214 : isolate_(isolate),
215 builder_(isolate),
216 gap_(nullptr),
217 indent_(0),
218 stack_() {
219 tojson_string_ = factory()->toJSON_string();
220}
221
222MaybeHandle<Object> JsonStringifier::Stringify(Handle<Object> object,
223 Handle<Object> replacer,
224 Handle<Object> gap) {
225 if (!InitializeReplacer(replacer)) return MaybeHandle<Object>();
226 if (!gap->IsUndefined(isolate_) && !InitializeGap(gap)) {
227 return MaybeHandle<Object>();
228 }
229 Result result = SerializeObject(object);
230 if (result == UNCHANGED) return factory()->undefined_value();
231 if (result == SUCCESS) return builder_.Finish();
232 DCHECK(result == EXCEPTION);
233 return MaybeHandle<Object>();
234}
235
236bool JsonStringifier::InitializeReplacer(Handle<Object> replacer) {
237 DCHECK(property_list_.is_null());
238 DCHECK(replacer_function_.is_null());
239 Maybe<bool> is_array = Object::IsArray(replacer);
240 if (is_array.IsNothing()) return false;
241 if (is_array.FromJust()) {
242 HandleScope handle_scope(isolate_);
243 Handle<OrderedHashSet> set = factory()->NewOrderedHashSet();
244 Handle<Object> length_obj;
245 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
246 isolate_, length_obj,
247 Object::GetLengthFromArrayLike(isolate_,
248 Handle<JSReceiver>::cast(replacer)),
249 false);
250 uint32_t length;
251 if (!length_obj->ToUint32(&length)) length = kMaxUInt32;
252 for (uint32_t i = 0; i < length; i++) {
253 Handle<Object> element;
254 Handle<String> key;
255 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
256 isolate_, element, Object::GetElement(isolate_, replacer, i), false);
257 if (element->IsNumber() || element->IsString()) {
258 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
259 isolate_, key, Object::ToString(isolate_, element), false);
260 } else if (element->IsJSValue()) {
261 Handle<Object> value(Handle<JSValue>::cast(element)->value(), isolate_);
262 if (value->IsNumber() || value->IsString()) {
263 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
264 isolate_, key, Object::ToString(isolate_, element), false);
265 }
266 }
267 if (key.is_null()) continue;
268 // Object keys are internalized, so do it here.
269 key = factory()->InternalizeString(key);
270 set = OrderedHashSet::Add(isolate_, set, key);
271 }
272 property_list_ = OrderedHashSet::ConvertToKeysArray(
273 isolate_, set, GetKeysConversion::kKeepNumbers);
274 property_list_ = handle_scope.CloseAndEscape(property_list_);
275 } else if (replacer->IsCallable()) {
276 replacer_function_ = Handle<JSReceiver>::cast(replacer);
277 }
278 return true;
279}
280
281bool JsonStringifier::InitializeGap(Handle<Object> gap) {
282 DCHECK_NULL(gap_);
283 HandleScope scope(isolate_);
284 if (gap->IsJSValue()) {
285 Handle<Object> value(Handle<JSValue>::cast(gap)->value(), isolate_);
286 if (value->IsString()) {
287 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap,
288 Object::ToString(isolate_, gap), false);
289 } else if (value->IsNumber()) {
290 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap,
291 Object::ToNumber(isolate_, gap), false);
292 }
293 }
294
295 if (gap->IsString()) {
296 Handle<String> gap_string = Handle<String>::cast(gap);
297 if (gap_string->length() > 0) {
298 int gap_length = std::min(gap_string->length(), 10);
299 gap_ = NewArray<uc16>(gap_length + 1);
300 String::WriteToFlat(*gap_string, gap_, 0, gap_length);
301 for (int i = 0; i < gap_length; i++) {
302 if (gap_[i] > String::kMaxOneByteCharCode) {
303 builder_.ChangeEncoding();
304 break;
305 }
306 }
307 gap_[gap_length] = '\0';
308 }
309 } else if (gap->IsNumber()) {
310 int num_value = DoubleToInt32(gap->Number());
311 if (num_value > 0) {
312 int gap_length = std::min(num_value, 10);
313 gap_ = NewArray<uc16>(gap_length + 1);
314 for (int i = 0; i < gap_length; i++) gap_[i] = ' ';
315 gap_[gap_length] = '\0';
316 }
317 }
318 return true;
319}
320
321MaybeHandle<Object> JsonStringifier::ApplyToJsonFunction(Handle<Object> object,
322 Handle<Object> key) {
323 HandleScope scope(isolate_);
324
325 Handle<Object> object_for_lookup = object;
326 if (object->IsBigInt()) {
327 ASSIGN_RETURN_ON_EXCEPTION(isolate_, object_for_lookup,
328 Object::ToObject(isolate_, object), Object);
329 }
330 DCHECK(object_for_lookup->IsJSReceiver());
331
332 // Retrieve toJSON function.
333 Handle<Object> fun;
334 {
335 LookupIterator it(isolate_, object_for_lookup, tojson_string_,
336 LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
337 ASSIGN_RETURN_ON_EXCEPTION(isolate_, fun, Object::GetProperty(&it), Object);
338 if (!fun->IsCallable()) return object;
339 }
340
341 // Call toJSON function.
342 if (key->IsSmi()) key = factory()->NumberToString(key);
343 Handle<Object> argv[] = {key};
344 ASSIGN_RETURN_ON_EXCEPTION(isolate_, object,
345 Execution::Call(isolate_, fun, object, 1, argv),
346 Object);
347 return scope.CloseAndEscape(object);
348}
349
350MaybeHandle<Object> JsonStringifier::ApplyReplacerFunction(
351 Handle<Object> value, Handle<Object> key, Handle<Object> initial_holder) {
352 HandleScope scope(isolate_);
353 if (key->IsSmi()) key = factory()->NumberToString(key);
354 Handle<Object> argv[] = {key, value};
355 Handle<JSReceiver> holder = CurrentHolder(value, initial_holder);
356 ASSIGN_RETURN_ON_EXCEPTION(
357 isolate_, value,
358 Execution::Call(isolate_, replacer_function_, holder, 2, argv), Object);
359 return scope.CloseAndEscape(value);
360}
361
362Handle<JSReceiver> JsonStringifier::CurrentHolder(
363 Handle<Object> value, Handle<Object> initial_holder) {
364 if (stack_.empty()) {
365 Handle<JSObject> holder =
366 factory()->NewJSObject(isolate_->object_function());
367 JSObject::AddProperty(isolate_, holder, factory()->empty_string(),
368 initial_holder, NONE);
369 return holder;
370 } else {
371 return Handle<JSReceiver>(JSReceiver::cast(*stack_.back().second),
372 isolate_);
373 }
374}
375
376JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object,
377 Handle<Object> key) {
378 StackLimitCheck check(isolate_);
379 if (check.HasOverflowed()) {
380 isolate_->StackOverflow();
381 return EXCEPTION;
382 }
383
384 {
385 DisallowHeapAllocation no_allocation;
386 for (size_t i = 0; i < stack_.size(); ++i) {
387 if (*stack_[i].second == *object) {
388 AllowHeapAllocation allow_to_return_error;
389 Handle<String> circle_description =
390 ConstructCircularStructureErrorMessage(key, i);
391 Handle<Object> error = factory()->NewTypeError(
392 MessageTemplate::kCircularStructure, circle_description);
393 isolate_->Throw(*error);
394 return EXCEPTION;
395 }
396 }
397 }
398 stack_.emplace_back(key, object);
399 return SUCCESS;
400}
401
402void JsonStringifier::StackPop() { stack_.pop_back(); }
403
404class CircularStructureMessageBuilder {
405 public:
406 explicit CircularStructureMessageBuilder(Isolate* isolate)
407 : builder_(isolate) {}
408
409 void AppendStartLine(Handle<Object> start_object) {
410 builder_.AppendCString(kStartPrefix);
411 builder_.AppendCString("starting at object with constructor ");
412 AppendConstructorName(start_object);
413 }
414
415 void AppendNormalLine(Handle<Object> key, Handle<Object> object) {
416 builder_.AppendCString(kLinePrefix);
417 AppendKey(key);
418 builder_.AppendCString(" -> object with constructor ");
419 AppendConstructorName(object);
420 }
421
422 void AppendClosingLine(Handle<Object> closing_key) {
423 builder_.AppendCString(kEndPrefix);
424 AppendKey(closing_key);
425 builder_.AppendCString(" closes the circle");
426 }
427
428 void AppendEllipsis() {
429 builder_.AppendCString(kLinePrefix);
430 builder_.AppendCString("...");
431 }
432
433 MaybeHandle<String> Finish() { return builder_.Finish(); }
434
435 private:
436 void AppendConstructorName(Handle<Object> object) {
437 builder_.AppendCharacter('\'');
438 Handle<String> constructor_name =
439 JSReceiver::GetConstructorName(Handle<JSReceiver>::cast(object));
440 builder_.AppendString(constructor_name);
441 builder_.AppendCharacter('\'');
442 }
443
444 // A key can either be a string, the empty string or a Smi.
445 void AppendKey(Handle<Object> key) {
446 if (key->IsSmi()) {
447 builder_.AppendCString("index ");
448 AppendSmi(Smi::cast(*key));
449 return;
450 }
451
452 CHECK(key->IsString());
453 Handle<String> key_as_string = Handle<String>::cast(key);
454 if (key_as_string->length() == 0) {
455 builder_.AppendCString("<anonymous>");
456 } else {
457 builder_.AppendCString("property '");
458 builder_.AppendString(key_as_string);
459 builder_.AppendCharacter('\'');
460 }
461 }
462
463 void AppendSmi(Smi smi) {
464 static const int kBufferSize = 100;
465 char chars[kBufferSize];
466 Vector<char> buffer(chars, kBufferSize);
467 builder_.AppendCString(IntToCString(smi->value(), buffer));
468 }
469
470 IncrementalStringBuilder builder_;
471 static constexpr const char* kStartPrefix = "\n --> ";
472 static constexpr const char* kEndPrefix = "\n --- ";
473 static constexpr const char* kLinePrefix = "\n | ";
474};
475
476Handle<String> JsonStringifier::ConstructCircularStructureErrorMessage(
477 Handle<Object> last_key, size_t start_index) {
478 DCHECK(start_index < stack_.size());
479 CircularStructureMessageBuilder builder(isolate_);
480
481 // We track the index to be printed next for better readability.
482 size_t index = start_index;
483 const size_t stack_size = stack_.size();
484
485 builder.AppendStartLine(stack_[index++].second);
486
487 // Append a maximum of kCircularErrorMessagePrefixCount normal lines.
488 const size_t prefix_end =
489 std::min(stack_size, index + kCircularErrorMessagePrefixCount);
490 for (; index < prefix_end; ++index) {
491 builder.AppendNormalLine(stack_[index].first, stack_[index].second);
492 }
493
494 // If the circle consists of too many objects, we skip them and just
495 // print an ellipsis.
496 if (stack_size > index + kCircularErrorMessagePostfixCount) {
497 builder.AppendEllipsis();
498 }
499
500 // Since we calculate the postfix lines from the back of the stack,
501 // we have to ensure that lines are not printed twice.
502 index = std::max(index, stack_size - kCircularErrorMessagePostfixCount);
503 for (; index < stack_size; ++index) {
504 builder.AppendNormalLine(stack_[index].first, stack_[index].second);
505 }
506
507 builder.AppendClosingLine(last_key);
508
509 Handle<String> result;
510 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, result, builder.Finish(),
511 factory()->empty_string());
512 return result;
513}
514
515template <bool deferred_string_key>
516JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
517 bool comma,
518 Handle<Object> key) {
519 StackLimitCheck interrupt_check(isolate_);
520 Handle<Object> initial_value = object;
521 if (interrupt_check.InterruptRequested() &&
522 isolate_->stack_guard()->HandleInterrupts()->IsException(isolate_)) {
523 return EXCEPTION;
524 }
525 if (object->IsJSReceiver() || object->IsBigInt()) {
526 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
527 isolate_, object, ApplyToJsonFunction(object, key), EXCEPTION);
528 }
529 if (!replacer_function_.is_null()) {
530 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
531 isolate_, object, ApplyReplacerFunction(object, key, initial_value),
532 EXCEPTION);
533 }
534
535 if (object->IsSmi()) {
536 if (deferred_string_key) SerializeDeferredKey(comma, key);
537 return SerializeSmi(Smi::cast(*object));
538 }
539
540 switch (HeapObject::cast(*object)->map()->instance_type()) {
541 case HEAP_NUMBER_TYPE:
542 case MUTABLE_HEAP_NUMBER_TYPE:
543 if (deferred_string_key) SerializeDeferredKey(comma, key);
544 return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
545 case BIGINT_TYPE:
546 isolate_->Throw(
547 *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON));
548 return EXCEPTION;
549 case ODDBALL_TYPE:
550 switch (Oddball::cast(*object)->kind()) {
551 case Oddball::kFalse:
552 if (deferred_string_key) SerializeDeferredKey(comma, key);
553 builder_.AppendCString("false");
554 return SUCCESS;
555 case Oddball::kTrue:
556 if (deferred_string_key) SerializeDeferredKey(comma, key);
557 builder_.AppendCString("true");
558 return SUCCESS;
559 case Oddball::kNull:
560 if (deferred_string_key) SerializeDeferredKey(comma, key);
561 builder_.AppendCString("null");
562 return SUCCESS;
563 default:
564 return UNCHANGED;
565 }
566 case JS_ARRAY_TYPE:
567 if (deferred_string_key) SerializeDeferredKey(comma, key);
568 return SerializeJSArray(Handle<JSArray>::cast(object), key);
569 case JS_VALUE_TYPE:
570 if (deferred_string_key) SerializeDeferredKey(comma, key);
571 return SerializeJSValue(Handle<JSValue>::cast(object), key);
572 case SYMBOL_TYPE:
573 return UNCHANGED;
574 default:
575 if (object->IsString()) {
576 if (deferred_string_key) SerializeDeferredKey(comma, key);
577 SerializeString(Handle<String>::cast(object));
578 return SUCCESS;
579 } else {
580 DCHECK(object->IsJSReceiver());
581 if (object->IsCallable()) return UNCHANGED;
582 // Go to slow path for global proxy and objects requiring access checks.
583 if (deferred_string_key) SerializeDeferredKey(comma, key);
584 if (object->IsJSProxy()) {
585 return SerializeJSProxy(Handle<JSProxy>::cast(object), key);
586 }
587 return SerializeJSObject(Handle<JSObject>::cast(object), key);
588 }
589 }
590
591 UNREACHABLE();
592}
593
594JsonStringifier::Result JsonStringifier::SerializeJSValue(
595 Handle<JSValue> object, Handle<Object> key) {
596 Object raw = object->value();
597 if (raw->IsString()) {
598 Handle<Object> value;
599 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
600 isolate_, value, Object::ToString(isolate_, object), EXCEPTION);
601 SerializeString(Handle<String>::cast(value));
602 } else if (raw->IsNumber()) {
603 Handle<Object> value;
604 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
605 isolate_, value, Object::ToNumber(isolate_, object), EXCEPTION);
606 if (value->IsSmi()) return SerializeSmi(Smi::cast(*value));
607 SerializeHeapNumber(Handle<HeapNumber>::cast(value));
608 } else if (raw->IsBigInt()) {
609 isolate_->Throw(
610 *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON));
611 return EXCEPTION;
612 } else if (raw->IsBoolean()) {
613 builder_.AppendCString(raw->IsTrue(isolate_) ? "true" : "false");
614 } else {
615 // ES6 24.3.2.1 step 10.c, serialize as an ordinary JSObject.
616 return SerializeJSObject(object, key);
617 }
618 return SUCCESS;
619}
620
621JsonStringifier::Result JsonStringifier::SerializeSmi(Smi object) {
622 static const int kBufferSize = 100;
623 char chars[kBufferSize];
624 Vector<char> buffer(chars, kBufferSize);
625 builder_.AppendCString(IntToCString(object->value(), buffer));
626 return SUCCESS;
627}
628
629JsonStringifier::Result JsonStringifier::SerializeDouble(double number) {
630 if (std::isinf(number) || std::isnan(number)) {
631 builder_.AppendCString("null");
632 return SUCCESS;
633 }
634 static const int kBufferSize = 100;
635 char chars[kBufferSize];
636 Vector<char> buffer(chars, kBufferSize);
637 builder_.AppendCString(DoubleToCString(number, buffer));
638 return SUCCESS;
639}
640
641JsonStringifier::Result JsonStringifier::SerializeJSArray(
642 Handle<JSArray> object, Handle<Object> key) {
643 HandleScope handle_scope(isolate_);
644 Result stack_push = StackPush(object, key);
645 if (stack_push != SUCCESS) return stack_push;
646 uint32_t length = 0;
647 CHECK(object->length()->ToArrayLength(&length));
648 DCHECK(!object->IsAccessCheckNeeded());
649 builder_.AppendCharacter('[');
650 Indent();
651 uint32_t i = 0;
652 if (replacer_function_.is_null()) {
653 switch (object->GetElementsKind()) {
654 case PACKED_SMI_ELEMENTS: {
655 Handle<FixedArray> elements(FixedArray::cast(object->elements()),
656 isolate_);
657 StackLimitCheck interrupt_check(isolate_);
658 while (i < length) {
659 if (interrupt_check.InterruptRequested() &&
660 isolate_->stack_guard()->HandleInterrupts()->IsException(
661 isolate_)) {
662 return EXCEPTION;
663 }
664 Separator(i == 0);
665 SerializeSmi(Smi::cast(elements->get(i)));
666 i++;
667 }
668 break;
669 }
670 case PACKED_DOUBLE_ELEMENTS: {
671 // Empty array is FixedArray but not FixedDoubleArray.
672 if (length == 0) break;
673 Handle<FixedDoubleArray> elements(
674 FixedDoubleArray::cast(object->elements()), isolate_);
675 StackLimitCheck interrupt_check(isolate_);
676 while (i < length) {
677 if (interrupt_check.InterruptRequested() &&
678 isolate_->stack_guard()->HandleInterrupts()->IsException(
679 isolate_)) {
680 return EXCEPTION;
681 }
682 Separator(i == 0);
683 SerializeDouble(elements->get_scalar(i));
684 i++;
685 }
686 break;
687 }
688 case PACKED_ELEMENTS: {
689 Handle<Object> old_length(object->length(), isolate_);
690 while (i < length) {
691 if (object->length() != *old_length ||
692 object->GetElementsKind() != PACKED_ELEMENTS) {
693 // Fall back to slow path.
694 break;
695 }
696 Separator(i == 0);
697 Result result = SerializeElement(
698 isolate_,
699 Handle<Object>(FixedArray::cast(object->elements())->get(i),
700 isolate_),
701 i);
702 if (result == UNCHANGED) {
703 builder_.AppendCString("null");
704 } else if (result != SUCCESS) {
705 return result;
706 }
707 i++;
708 }
709 break;
710 }
711 // The FAST_HOLEY_* cases could be handled in a faster way. They resemble
712 // the non-holey cases except that a lookup is necessary for holes.
713 default:
714 break;
715 }
716 }
717 if (i < length) {
718 // Slow path for non-fast elements and fall-back in edge case.
719 Result result = SerializeArrayLikeSlow(object, i, length);
720 if (result != SUCCESS) return result;
721 }
722 Unindent();
723 if (length > 0) NewLine();
724 builder_.AppendCharacter(']');
725 StackPop();
726 return SUCCESS;
727}
728
729JsonStringifier::Result JsonStringifier::SerializeArrayLikeSlow(
730 Handle<JSReceiver> object, uint32_t start, uint32_t length) {
731 // We need to write out at least two characters per array element.
732 static const int kMaxSerializableArrayLength = String::kMaxLength / 2;
733 if (length > kMaxSerializableArrayLength) {
734 isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError());
735 return EXCEPTION;
736 }
737 for (uint32_t i = start; i < length; i++) {
738 Separator(i == 0);
739 Handle<Object> element;
740 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
741 isolate_, element, JSReceiver::GetElement(isolate_, object, i),
742 EXCEPTION);
743 Result result = SerializeElement(isolate_, element, i);
744 if (result == SUCCESS) continue;
745 if (result == UNCHANGED) {
746 // Detect overflow sooner for large sparse arrays.
747 if (builder_.HasOverflowed()) return EXCEPTION;
748 builder_.AppendCString("null");
749 } else {
750 return result;
751 }
752 }
753 return SUCCESS;
754}
755
756JsonStringifier::Result JsonStringifier::SerializeJSObject(
757 Handle<JSObject> object, Handle<Object> key) {
758 HandleScope handle_scope(isolate_);
759 Result stack_push = StackPush(object, key);
760 if (stack_push != SUCCESS) return stack_push;
761
762 if (property_list_.is_null() &&
763 !object->map()->IsCustomElementsReceiverMap() &&
764 object->HasFastProperties() &&
765 (object->elements() == ReadOnlyRoots(isolate_).empty_fixed_array() ||
766 object->elements() ==
767 ReadOnlyRoots(isolate_).empty_slow_element_dictionary())) {
768 DCHECK(!object->IsJSGlobalProxy());
769 DCHECK(!object->HasIndexedInterceptor());
770 DCHECK(!object->HasNamedInterceptor());
771 Handle<Map> map(object->map(), isolate_);
772 builder_.AppendCharacter('{');
773 Indent();
774 bool comma = false;
775 for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
776 Handle<Name> name(map->instance_descriptors()->GetKey(i), isolate_);
777 // TODO(rossberg): Should this throw?
778 if (!name->IsString()) continue;
779 Handle<String> key = Handle<String>::cast(name);
780 PropertyDetails details = map->instance_descriptors()->GetDetails(i);
781 if (details.IsDontEnum()) continue;
782 Handle<Object> property;
783 if (details.location() == kField && *map == object->map()) {
784 DCHECK_EQ(kData, details.kind());
785 FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
786 property = JSObject::FastPropertyAt(object, details.representation(),
787 field_index);
788 } else {
789 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
790 isolate_, property,
791 Object::GetPropertyOrElement(isolate_, object, key), EXCEPTION);
792 }
793 Result result = SerializeProperty(property, comma, key);
794 if (!comma && result == SUCCESS) comma = true;
795 if (result == EXCEPTION) return result;
796 }
797 Unindent();
798 if (comma) NewLine();
799 builder_.AppendCharacter('}');
800 } else {
801 Result result = SerializeJSReceiverSlow(object);
802 if (result != SUCCESS) return result;
803 }
804 StackPop();
805 return SUCCESS;
806}
807
808JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow(
809 Handle<JSReceiver> object) {
810 Handle<FixedArray> contents = property_list_;
811 if (contents.is_null()) {
812 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
813 isolate_, contents,
814 KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
815 ENUMERABLE_STRINGS,
816 GetKeysConversion::kConvertToString),
817 EXCEPTION);
818 }
819 builder_.AppendCharacter('{');
820 Indent();
821 bool comma = false;
822 for (int i = 0; i < contents->length(); i++) {
823 Handle<String> key(String::cast(contents->get(i)), isolate_);
824 Handle<Object> property;
825 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
826 isolate_, property, Object::GetPropertyOrElement(isolate_, object, key),
827 EXCEPTION);
828 Result result = SerializeProperty(property, comma, key);
829 if (!comma && result == SUCCESS) comma = true;
830 if (result == EXCEPTION) return result;
831 }
832 Unindent();
833 if (comma) NewLine();
834 builder_.AppendCharacter('}');
835 return SUCCESS;
836}
837
838JsonStringifier::Result JsonStringifier::SerializeJSProxy(
839 Handle<JSProxy> object, Handle<Object> key) {
840 HandleScope scope(isolate_);
841 Result stack_push = StackPush(object, key);
842 if (stack_push != SUCCESS) return stack_push;
843 Maybe<bool> is_array = Object::IsArray(object);
844 if (is_array.IsNothing()) return EXCEPTION;
845 if (is_array.FromJust()) {
846 Handle<Object> length_object;
847 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
848 isolate_, length_object,
849 Object::GetLengthFromArrayLike(isolate_,
850 Handle<JSReceiver>::cast(object)),
851 EXCEPTION);
852 uint32_t length;
853 if (!length_object->ToUint32(&length)) {
854 // Technically, we need to be able to handle lengths outside the
855 // uint32_t range. However, we would run into string size overflow
856 // if we tried to stringify such an array.
857 isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError());
858 return EXCEPTION;
859 }
860 builder_.AppendCharacter('[');
861 Indent();
862 Result result = SerializeArrayLikeSlow(object, 0, length);
863 if (result != SUCCESS) return result;
864 Unindent();
865 if (length > 0) NewLine();
866 builder_.AppendCharacter(']');
867 } else {
868 Result result = SerializeJSReceiverSlow(object);
869 if (result != SUCCESS) return result;
870 }
871 StackPop();
872 return SUCCESS;
873}
874
875template <typename SrcChar, typename DestChar>
876void JsonStringifier::SerializeStringUnchecked_(
877 Vector<const SrcChar> src,
878 IncrementalStringBuilder::NoExtend<DestChar>* dest) {
879 // Assert that uc16 character is not truncated down to 8 bit.
880 // The <uc16, char> version of this method must not be called.
881 DCHECK(sizeof(DestChar) >= sizeof(SrcChar));
882 for (int i = 0; i < src.length(); i++) {
883 SrcChar c = src[i];
884 if (DoNotEscape(c)) {
885 dest->Append(c);
886 } else if (FLAG_harmony_json_stringify && c >= 0xD800 && c <= 0xDFFF) {
887 // The current character is a surrogate.
888 if (c <= 0xDBFF) {
889 // The current character is a leading surrogate.
890 if (i + 1 < src.length()) {
891 // There is a next character.
892 SrcChar next = src[i + 1];
893 if (next >= 0xDC00 && next <= 0xDFFF) {
894 // The next character is a trailing surrogate, meaning this is a
895 // surrogate pair.
896 dest->Append(c);
897 dest->Append(next);
898 i++;
899 } else {
900 // The next character is not a trailing surrogate. Thus, the
901 // current character is a lone leading surrogate.
902 dest->AppendCString("\\u");
903 char* const hex = DoubleToRadixCString(c, 16);
904 dest->AppendCString(hex);
905 DeleteArray(hex);
906 }
907 } else {
908 // There is no next character. Thus, the current character is a lone
909 // leading surrogate.
910 dest->AppendCString("\\u");
911 char* const hex = DoubleToRadixCString(c, 16);
912 dest->AppendCString(hex);
913 DeleteArray(hex);
914 }
915 } else {
916 // The current character is a lone trailing surrogate. (If it had been
917 // preceded by a leading surrogate, we would've ended up in the other
918 // branch earlier on, and the current character would've been handled
919 // as part of the surrogate pair already.)
920 dest->AppendCString("\\u");
921 char* const hex = DoubleToRadixCString(c, 16);
922 dest->AppendCString(hex);
923 DeleteArray(hex);
924 }
925 } else {
926 dest->AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
927 }
928 }
929}
930
931template <typename SrcChar, typename DestChar>
932void JsonStringifier::SerializeString_(Handle<String> string) {
933 int length = string->length();
934 builder_.Append<uint8_t, DestChar>('"');
935 // We might be able to fit the whole escaped string in the current string
936 // part, or we might need to allocate.
937 if (int worst_case_length = builder_.EscapedLengthIfCurrentPartFits(length)) {
938 DisallowHeapAllocation no_gc;
939 Vector<const SrcChar> vector = string->GetCharVector<SrcChar>(no_gc);
940 IncrementalStringBuilder::NoExtendBuilder<DestChar> no_extend(
941 &builder_, worst_case_length, no_gc);
942 SerializeStringUnchecked_(vector, &no_extend);
943 } else {
944 FlatStringReader reader(isolate_, string);
945 for (int i = 0; i < reader.length(); i++) {
946 SrcChar c = reader.Get<SrcChar>(i);
947 if (DoNotEscape(c)) {
948 builder_.Append<SrcChar, DestChar>(c);
949 } else if (FLAG_harmony_json_stringify && c >= 0xD800 && c <= 0xDFFF) {
950 // The current character is a surrogate.
951 if (c <= 0xDBFF) {
952 // The current character is a leading surrogate.
953 if (i + 1 < reader.length()) {
954 // There is a next character.
955 SrcChar next = reader.Get<SrcChar>(i + 1);
956 if (next >= 0xDC00 && next <= 0xDFFF) {
957 // The next character is a trailing surrogate, meaning this is a
958 // surrogate pair.
959 builder_.Append<SrcChar, DestChar>(c);
960 builder_.Append<SrcChar, DestChar>(next);
961 i++;
962 } else {
963 // The next character is not a trailing surrogate. Thus, the
964 // current character is a lone leading surrogate.
965 builder_.AppendCString("\\u");
966 char* const hex = DoubleToRadixCString(c, 16);
967 builder_.AppendCString(hex);
968 DeleteArray(hex);
969 }
970 } else {
971 // There is no next character. Thus, the current character is a
972 // lone leading surrogate.
973 builder_.AppendCString("\\u");
974 char* const hex = DoubleToRadixCString(c, 16);
975 builder_.AppendCString(hex);
976 DeleteArray(hex);
977 }
978 } else {
979 // The current character is a lone trailing surrogate. (If it had
980 // been preceded by a leading surrogate, we would've ended up in the
981 // other branch earlier on, and the current character would've been
982 // handled as part of the surrogate pair already.)
983 builder_.AppendCString("\\u");
984 char* const hex = DoubleToRadixCString(c, 16);
985 builder_.AppendCString(hex);
986 DeleteArray(hex);
987 }
988 } else {
989 builder_.AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
990 }
991 }
992 }
993 builder_.Append<uint8_t, DestChar>('"');
994}
995
996template <>
997bool JsonStringifier::DoNotEscape(uint8_t c) {
998 // https://tc39.github.io/ecma262/#table-json-single-character-escapes
999 return c >= 0x23 && c <= 0x7E && c != 0x5C;
1000}
1001
1002template <>
1003bool JsonStringifier::DoNotEscape(uint16_t c) {
1004 // https://tc39.github.io/ecma262/#table-json-single-character-escapes
1005 return c >= 0x23 && c != 0x5C && c != 0x7F &&
1006 (!FLAG_harmony_json_stringify || (c < 0xD800 || c > 0xDFFF));
1007}
1008
1009void JsonStringifier::NewLine() {
1010 if (gap_ == nullptr) return;
1011 builder_.AppendCharacter('\n');
1012 for (int i = 0; i < indent_; i++) builder_.AppendCString(gap_);
1013}
1014
1015void JsonStringifier::Separator(bool first) {
1016 if (!first) builder_.AppendCharacter(',');
1017 NewLine();
1018}
1019
1020void JsonStringifier::SerializeDeferredKey(bool deferred_comma,
1021 Handle<Object> deferred_key) {
1022 Separator(!deferred_comma);
1023 SerializeString(Handle<String>::cast(deferred_key));
1024 builder_.AppendCharacter(':');
1025 if (gap_ != nullptr) builder_.AppendCharacter(' ');
1026}
1027
1028void JsonStringifier::SerializeString(Handle<String> object) {
1029 object = String::Flatten(isolate_, object);
1030 if (builder_.CurrentEncoding() == String::ONE_BYTE_ENCODING) {
1031 if (String::IsOneByteRepresentationUnderneath(*object)) {
1032 SerializeString_<uint8_t, uint8_t>(object);
1033 } else {
1034 builder_.ChangeEncoding();
1035 SerializeString(object);
1036 }
1037 } else {
1038 if (String::IsOneByteRepresentationUnderneath(*object)) {
1039 SerializeString_<uint8_t, uc16>(object);
1040 } else {
1041 SerializeString_<uc16, uc16>(object);
1042 }
1043 }
1044}
1045
1046} // namespace internal
1047} // namespace v8
1048