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