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/base/strings.h"
8 #include "src/common/message-template.h"
9 #include "src/numbers/conversions.h"
10 #include "src/objects/heap-number-inl.h"
11 #include "src/objects/js-array-inl.h"
12 #include "src/objects/lookup.h"
13 #include "src/objects/objects-inl.h"
14 #include "src/objects/oddball-inl.h"
15 #include "src/objects/ordered-hash-table.h"
16 #include "src/objects/smi.h"
17 #include "src/strings/string-builder-inl.h"
18 #include "src/utils/utils.h"
19
20 namespace v8 {
21 namespace internal {
22
23 class JsonStringifier {
24 public:
25 explicit JsonStringifier(Isolate* isolate);
26
~JsonStringifier()27 ~JsonStringifier() { DeleteArray(gap_); }
28
29 V8_WARN_UNUSED_RESULT MaybeHandle<Object> Stringify(Handle<Object> object,
30 Handle<Object> replacer,
31 Handle<Object> gap);
32
33 private:
34 enum Result { UNCHANGED, SUCCESS, EXCEPTION };
35
36 bool InitializeReplacer(Handle<Object> replacer);
37 bool InitializeGap(Handle<Object> gap);
38
39 V8_WARN_UNUSED_RESULT MaybeHandle<Object> ApplyToJsonFunction(
40 Handle<Object> object, Handle<Object> key);
41 V8_WARN_UNUSED_RESULT MaybeHandle<Object> ApplyReplacerFunction(
42 Handle<Object> value, Handle<Object> key, Handle<Object> initial_holder);
43
44 // Entry point to serialize the object.
SerializeObject(Handle<Object> obj)45 V8_INLINE Result SerializeObject(Handle<Object> obj) {
46 return Serialize_<false>(obj, false, factory()->empty_string());
47 }
48
49 // Serialize an array element.
50 // The index may serve as argument for the toJSON function.
SerializeElement(Isolate * isolate,Handle<Object> object,int i)51 V8_INLINE Result SerializeElement(Isolate* isolate, Handle<Object> object,
52 int i) {
53 return Serialize_<false>(object, false,
54 Handle<Object>(Smi::FromInt(i), isolate));
55 }
56
57 // Serialize a object property.
58 // The key may or may not be serialized depending on the property.
59 // The key may also serve as argument for the toJSON function.
SerializeProperty(Handle<Object> object,bool deferred_comma,Handle<String> deferred_key)60 V8_INLINE Result SerializeProperty(Handle<Object> object, bool deferred_comma,
61 Handle<String> deferred_key) {
62 DCHECK(!deferred_key.is_null());
63 return Serialize_<true>(object, deferred_comma, deferred_key);
64 }
65
66 template <bool deferred_string_key>
67 Result Serialize_(Handle<Object> object, bool comma, Handle<Object> key);
68
69 V8_INLINE void SerializeDeferredKey(bool deferred_comma,
70 Handle<Object> deferred_key);
71
72 Result SerializeSmi(Smi object);
73
74 Result SerializeDouble(double number);
SerializeHeapNumber(Handle<HeapNumber> object)75 V8_INLINE Result SerializeHeapNumber(Handle<HeapNumber> object) {
76 return SerializeDouble(object->value());
77 }
78
79 Result SerializeJSPrimitiveWrapper(Handle<JSPrimitiveWrapper> object,
80 Handle<Object> key);
81
82 V8_INLINE Result SerializeJSArray(Handle<JSArray> object, Handle<Object> key);
83 V8_INLINE Result SerializeJSObject(Handle<JSObject> object,
84 Handle<Object> key);
85
86 Result SerializeJSProxy(Handle<JSProxy> object, Handle<Object> key);
87 Result SerializeJSReceiverSlow(Handle<JSReceiver> object);
88 Result SerializeArrayLikeSlow(Handle<JSReceiver> object, uint32_t start,
89 uint32_t length);
90
91 void SerializeString(Handle<String> object);
92
93 template <typename SrcChar, typename DestChar>
94 V8_INLINE static void SerializeStringUnchecked_(
95 base::Vector<const SrcChar> src,
96 IncrementalStringBuilder::NoExtend<DestChar>* dest);
97
98 template <typename SrcChar, typename DestChar>
99 V8_INLINE void SerializeString_(Handle<String> string);
100
101 template <typename Char>
102 V8_INLINE static bool DoNotEscape(Char c);
103
104 V8_INLINE void NewLine();
Indent()105 V8_INLINE void Indent() { indent_++; }
Unindent()106 V8_INLINE void Unindent() { indent_--; }
107 V8_INLINE void Separator(bool first);
108
109 Handle<JSReceiver> CurrentHolder(Handle<Object> value,
110 Handle<Object> inital_holder);
111
112 Result StackPush(Handle<Object> object, Handle<Object> key);
113 void StackPop();
114
115 // Uses the current stack_ to provide a detailed error message of
116 // the objects involved in the circular structure.
117 Handle<String> ConstructCircularStructureErrorMessage(Handle<Object> last_key,
118 size_t start_index);
119 // The prefix and postfix count do NOT include the starting and
120 // closing lines of the error message.
121 static const int kCircularErrorMessagePrefixCount = 2;
122 static const int kCircularErrorMessagePostfixCount = 1;
123
factory()124 Factory* factory() { return isolate_->factory(); }
125
126 Isolate* isolate_;
127 IncrementalStringBuilder builder_;
128 Handle<String> tojson_string_;
129 Handle<FixedArray> property_list_;
130 Handle<JSReceiver> replacer_function_;
131 base::uc16* gap_;
132 int indent_;
133
134 using KeyObject = std::pair<Handle<Object>, Handle<Object>>;
135 std::vector<KeyObject> stack_;
136
137 static const int kJsonEscapeTableEntrySize = 8;
138 static const char* const JsonEscapeTable;
139 };
140
JsonStringify(Isolate * isolate,Handle<Object> object,Handle<Object> replacer,Handle<Object> gap)141 MaybeHandle<Object> JsonStringify(Isolate* isolate, Handle<Object> object,
142 Handle<Object> replacer, Handle<Object> gap) {
143 JsonStringifier stringifier(isolate);
144 return stringifier.Stringify(object, replacer, gap);
145 }
146
147 // Translation table to escape Latin1 characters.
148 // Table entries start at a multiple of 8 and are null-terminated.
149 const char* const JsonStringifier::JsonEscapeTable =
150 "\\u0000\0 \\u0001\0 \\u0002\0 \\u0003\0 "
151 "\\u0004\0 \\u0005\0 \\u0006\0 \\u0007\0 "
152 "\\b\0 \\t\0 \\n\0 \\u000b\0 "
153 "\\f\0 \\r\0 \\u000e\0 \\u000f\0 "
154 "\\u0010\0 \\u0011\0 \\u0012\0 \\u0013\0 "
155 "\\u0014\0 \\u0015\0 \\u0016\0 \\u0017\0 "
156 "\\u0018\0 \\u0019\0 \\u001a\0 \\u001b\0 "
157 "\\u001c\0 \\u001d\0 \\u001e\0 \\u001f\0 "
158 " \0 !\0 \\\"\0 #\0 "
159 "$\0 %\0 &\0 '\0 "
160 "(\0 )\0 *\0 +\0 "
161 ",\0 -\0 .\0 /\0 "
162 "0\0 1\0 2\0 3\0 "
163 "4\0 5\0 6\0 7\0 "
164 "8\0 9\0 :\0 ;\0 "
165 "<\0 =\0 >\0 ?\0 "
166 "@\0 A\0 B\0 C\0 "
167 "D\0 E\0 F\0 G\0 "
168 "H\0 I\0 J\0 K\0 "
169 "L\0 M\0 N\0 O\0 "
170 "P\0 Q\0 R\0 S\0 "
171 "T\0 U\0 V\0 W\0 "
172 "X\0 Y\0 Z\0 [\0 "
173 "\\\\\0 ]\0 ^\0 _\0 "
174 "`\0 a\0 b\0 c\0 "
175 "d\0 e\0 f\0 g\0 "
176 "h\0 i\0 j\0 k\0 "
177 "l\0 m\0 n\0 o\0 "
178 "p\0 q\0 r\0 s\0 "
179 "t\0 u\0 v\0 w\0 "
180 "x\0 y\0 z\0 {\0 "
181 "|\0 }\0 ~\0 \x7F\0 "
182 "\x80\0 \x81\0 \x82\0 \x83\0 "
183 "\x84\0 \x85\0 \x86\0 \x87\0 "
184 "\x88\0 \x89\0 \x8A\0 \x8B\0 "
185 "\x8C\0 \x8D\0 \x8E\0 \x8F\0 "
186 "\x90\0 \x91\0 \x92\0 \x93\0 "
187 "\x94\0 \x95\0 \x96\0 \x97\0 "
188 "\x98\0 \x99\0 \x9A\0 \x9B\0 "
189 "\x9C\0 \x9D\0 \x9E\0 \x9F\0 "
190 "\xA0\0 \xA1\0 \xA2\0 \xA3\0 "
191 "\xA4\0 \xA5\0 \xA6\0 \xA7\0 "
192 "\xA8\0 \xA9\0 \xAA\0 \xAB\0 "
193 "\xAC\0 \xAD\0 \xAE\0 \xAF\0 "
194 "\xB0\0 \xB1\0 \xB2\0 \xB3\0 "
195 "\xB4\0 \xB5\0 \xB6\0 \xB7\0 "
196 "\xB8\0 \xB9\0 \xBA\0 \xBB\0 "
197 "\xBC\0 \xBD\0 \xBE\0 \xBF\0 "
198 "\xC0\0 \xC1\0 \xC2\0 \xC3\0 "
199 "\xC4\0 \xC5\0 \xC6\0 \xC7\0 "
200 "\xC8\0 \xC9\0 \xCA\0 \xCB\0 "
201 "\xCC\0 \xCD\0 \xCE\0 \xCF\0 "
202 "\xD0\0 \xD1\0 \xD2\0 \xD3\0 "
203 "\xD4\0 \xD5\0 \xD6\0 \xD7\0 "
204 "\xD8\0 \xD9\0 \xDA\0 \xDB\0 "
205 "\xDC\0 \xDD\0 \xDE\0 \xDF\0 "
206 "\xE0\0 \xE1\0 \xE2\0 \xE3\0 "
207 "\xE4\0 \xE5\0 \xE6\0 \xE7\0 "
208 "\xE8\0 \xE9\0 \xEA\0 \xEB\0 "
209 "\xEC\0 \xED\0 \xEE\0 \xEF\0 "
210 "\xF0\0 \xF1\0 \xF2\0 \xF3\0 "
211 "\xF4\0 \xF5\0 \xF6\0 \xF7\0 "
212 "\xF8\0 \xF9\0 \xFA\0 \xFB\0 "
213 "\xFC\0 \xFD\0 \xFE\0 \xFF\0 ";
214
JsonStringifier(Isolate * isolate)215 JsonStringifier::JsonStringifier(Isolate* isolate)
216 : isolate_(isolate),
217 builder_(isolate),
218 gap_(nullptr),
219 indent_(0),
220 stack_() {
221 tojson_string_ = factory()->toJSON_string();
222 }
223
Stringify(Handle<Object> object,Handle<Object> replacer,Handle<Object> gap)224 MaybeHandle<Object> JsonStringifier::Stringify(Handle<Object> object,
225 Handle<Object> replacer,
226 Handle<Object> gap) {
227 if (!InitializeReplacer(replacer)) return MaybeHandle<Object>();
228 if (!gap->IsUndefined(isolate_) && !InitializeGap(gap)) {
229 return MaybeHandle<Object>();
230 }
231 Result result = SerializeObject(object);
232 if (result == UNCHANGED) return factory()->undefined_value();
233 if (result == SUCCESS) return builder_.Finish();
234 DCHECK(result == EXCEPTION);
235 return MaybeHandle<Object>();
236 }
237
InitializeReplacer(Handle<Object> replacer)238 bool JsonStringifier::InitializeReplacer(Handle<Object> replacer) {
239 DCHECK(property_list_.is_null());
240 DCHECK(replacer_function_.is_null());
241 Maybe<bool> is_array = Object::IsArray(replacer);
242 if (is_array.IsNothing()) return false;
243 if (is_array.FromJust()) {
244 HandleScope handle_scope(isolate_);
245 Handle<OrderedHashSet> set = factory()->NewOrderedHashSet();
246 Handle<Object> length_obj;
247 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
248 isolate_, length_obj,
249 Object::GetLengthFromArrayLike(isolate_,
250 Handle<JSReceiver>::cast(replacer)),
251 false);
252 uint32_t length;
253 if (!length_obj->ToUint32(&length)) length = kMaxUInt32;
254 for (uint32_t i = 0; i < length; i++) {
255 Handle<Object> element;
256 Handle<String> key;
257 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
258 isolate_, element, Object::GetElement(isolate_, replacer, i), false);
259 if (element->IsNumber() || element->IsString()) {
260 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
261 isolate_, key, Object::ToString(isolate_, element), false);
262 } else if (element->IsJSPrimitiveWrapper()) {
263 Handle<Object> value(Handle<JSPrimitiveWrapper>::cast(element)->value(),
264 isolate_);
265 if (value->IsNumber() || value->IsString()) {
266 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
267 isolate_, key, Object::ToString(isolate_, element), false);
268 }
269 }
270 if (key.is_null()) continue;
271 // Object keys are internalized, so do it here.
272 key = factory()->InternalizeString(key);
273 MaybeHandle<OrderedHashSet> set_candidate =
274 OrderedHashSet::Add(isolate_, set, key);
275 if (!set_candidate.ToHandle(&set)) {
276 return false;
277 }
278 }
279 property_list_ = OrderedHashSet::ConvertToKeysArray(
280 isolate_, set, GetKeysConversion::kKeepNumbers);
281 property_list_ = handle_scope.CloseAndEscape(property_list_);
282 } else if (replacer->IsCallable()) {
283 replacer_function_ = Handle<JSReceiver>::cast(replacer);
284 }
285 return true;
286 }
287
InitializeGap(Handle<Object> gap)288 bool JsonStringifier::InitializeGap(Handle<Object> gap) {
289 DCHECK_NULL(gap_);
290 HandleScope scope(isolate_);
291 if (gap->IsJSPrimitiveWrapper()) {
292 Handle<Object> value(Handle<JSPrimitiveWrapper>::cast(gap)->value(),
293 isolate_);
294 if (value->IsString()) {
295 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap,
296 Object::ToString(isolate_, gap), false);
297 } else if (value->IsNumber()) {
298 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap,
299 Object::ToNumber(isolate_, gap), false);
300 }
301 }
302
303 if (gap->IsString()) {
304 Handle<String> gap_string = Handle<String>::cast(gap);
305 if (gap_string->length() > 0) {
306 int gap_length = std::min(gap_string->length(), 10);
307 gap_ = NewArray<base::uc16>(gap_length + 1);
308 String::WriteToFlat(*gap_string, gap_, 0, gap_length);
309 for (int i = 0; i < gap_length; i++) {
310 if (gap_[i] > String::kMaxOneByteCharCode) {
311 builder_.ChangeEncoding();
312 break;
313 }
314 }
315 gap_[gap_length] = '\0';
316 }
317 } else if (gap->IsNumber()) {
318 double value = std::min(gap->Number(), 10.0);
319 if (value > 0) {
320 int gap_length = DoubleToInt32(value);
321 gap_ = NewArray<base::uc16>(gap_length + 1);
322 for (int i = 0; i < gap_length; i++) gap_[i] = ' ';
323 gap_[gap_length] = '\0';
324 }
325 }
326 return true;
327 }
328
ApplyToJsonFunction(Handle<Object> object,Handle<Object> key)329 MaybeHandle<Object> JsonStringifier::ApplyToJsonFunction(Handle<Object> object,
330 Handle<Object> key) {
331 HandleScope scope(isolate_);
332
333 // Retrieve toJSON function. The LookupIterator automatically handles
334 // the ToObject() equivalent ("GetRoot") if {object} is a BigInt.
335 Handle<Object> fun;
336 LookupIterator it(isolate_, object, tojson_string_,
337 LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
338 ASSIGN_RETURN_ON_EXCEPTION(isolate_, fun, Object::GetProperty(&it), Object);
339 if (!fun->IsCallable()) return object;
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
ApplyReplacerFunction(Handle<Object> value,Handle<Object> key,Handle<Object> initial_holder)350 MaybeHandle<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
CurrentHolder(Handle<Object> value,Handle<Object> initial_holder)362 Handle<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
StackPush(Handle<Object> object,Handle<Object> key)376 JsonStringifier::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 DisallowGarbageCollection no_gc;
386 for (size_t i = 0; i < stack_.size(); ++i) {
387 if (*stack_[i].second == *object) {
388 AllowGarbageCollection 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
StackPop()402 void JsonStringifier::StackPop() { stack_.pop_back(); }
403
404 class CircularStructureMessageBuilder {
405 public:
CircularStructureMessageBuilder(Isolate * isolate)406 explicit CircularStructureMessageBuilder(Isolate* isolate)
407 : builder_(isolate) {}
408
AppendStartLine(Handle<Object> start_object)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
AppendNormalLine(Handle<Object> key,Handle<Object> object)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
AppendClosingLine(Handle<Object> closing_key)422 void AppendClosingLine(Handle<Object> closing_key) {
423 builder_.AppendCString(kEndPrefix);
424 AppendKey(closing_key);
425 builder_.AppendCString(" closes the circle");
426 }
427
AppendEllipsis()428 void AppendEllipsis() {
429 builder_.AppendCString(kLinePrefix);
430 builder_.AppendCString("...");
431 }
432
Finish()433 MaybeHandle<String> Finish() { return builder_.Finish(); }
434
435 private:
AppendConstructorName(Handle<Object> object)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.
AppendKey(Handle<Object> key)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
AppendSmi(Smi smi)463 void AppendSmi(Smi smi) {
464 static const int kBufferSize = 100;
465 char chars[kBufferSize];
466 base::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
ConstructCircularStructureErrorMessage(Handle<Object> last_key,size_t start_index)476 Handle<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
515 template <bool deferred_string_key>
Serialize_(Handle<Object> object,bool comma,Handle<Object> key)516 JsonStringifier::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 if (deferred_string_key) SerializeDeferredKey(comma, key);
543 return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
544 case BIGINT_TYPE:
545 isolate_->Throw(
546 *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON));
547 return EXCEPTION;
548 case ODDBALL_TYPE:
549 switch (Oddball::cast(*object).kind()) {
550 case Oddball::kFalse:
551 if (deferred_string_key) SerializeDeferredKey(comma, key);
552 builder_.AppendCString("false");
553 return SUCCESS;
554 case Oddball::kTrue:
555 if (deferred_string_key) SerializeDeferredKey(comma, key);
556 builder_.AppendCString("true");
557 return SUCCESS;
558 case Oddball::kNull:
559 if (deferred_string_key) SerializeDeferredKey(comma, key);
560 builder_.AppendCString("null");
561 return SUCCESS;
562 default:
563 return UNCHANGED;
564 }
565 case JS_ARRAY_TYPE:
566 if (deferred_string_key) SerializeDeferredKey(comma, key);
567 return SerializeJSArray(Handle<JSArray>::cast(object), key);
568 case JS_PRIMITIVE_WRAPPER_TYPE:
569 if (deferred_string_key) SerializeDeferredKey(comma, key);
570 return SerializeJSPrimitiveWrapper(
571 Handle<JSPrimitiveWrapper>::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
SerializeJSPrimitiveWrapper(Handle<JSPrimitiveWrapper> object,Handle<Object> key)594 JsonStringifier::Result JsonStringifier::SerializeJSPrimitiveWrapper(
595 Handle<JSPrimitiveWrapper> 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
SerializeSmi(Smi object)621 JsonStringifier::Result JsonStringifier::SerializeSmi(Smi object) {
622 static const int kBufferSize = 100;
623 char chars[kBufferSize];
624 base::Vector<char> buffer(chars, kBufferSize);
625 builder_.AppendCString(IntToCString(object.value(), buffer));
626 return SUCCESS;
627 }
628
SerializeDouble(double number)629 JsonStringifier::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 base::Vector<char> buffer(chars, kBufferSize);
637 builder_.AppendCString(DoubleToCString(number, buffer));
638 return SUCCESS;
639 }
640
SerializeJSArray(Handle<JSArray> object,Handle<Object> key)641 JsonStringifier::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
SerializeArrayLikeSlow(Handle<JSReceiver> object,uint32_t start,uint32_t length)729 JsonStringifier::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
SerializeJSObject(Handle<JSObject> object,Handle<Object> key)756 JsonStringifier::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 (InternalIndex i : map->IterateOwnDescriptors()) {
776 Handle<Name> name(map->instance_descriptors(isolate_).GetKey(i),
777 isolate_);
778 // TODO(rossberg): Should this throw?
779 if (!name->IsString()) continue;
780 Handle<String> key = Handle<String>::cast(name);
781 PropertyDetails details =
782 map->instance_descriptors(isolate_).GetDetails(i);
783 if (details.IsDontEnum()) continue;
784 Handle<Object> property;
785 if (details.location() == PropertyLocation::kField &&
786 *map == object->map()) {
787 DCHECK_EQ(kData, details.kind());
788 FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
789 property = JSObject::FastPropertyAt(object, details.representation(),
790 field_index);
791 } else {
792 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
793 isolate_, property,
794 Object::GetPropertyOrElement(isolate_, object, key), EXCEPTION);
795 }
796 Result result = SerializeProperty(property, comma, key);
797 if (!comma && result == SUCCESS) comma = true;
798 if (result == EXCEPTION) return result;
799 }
800 Unindent();
801 if (comma) NewLine();
802 builder_.AppendCharacter('}');
803 } else {
804 Result result = SerializeJSReceiverSlow(object);
805 if (result != SUCCESS) return result;
806 }
807 StackPop();
808 return SUCCESS;
809 }
810
SerializeJSReceiverSlow(Handle<JSReceiver> object)811 JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow(
812 Handle<JSReceiver> object) {
813 Handle<FixedArray> contents = property_list_;
814 if (contents.is_null()) {
815 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
816 isolate_, contents,
817 KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
818 ENUMERABLE_STRINGS,
819 GetKeysConversion::kConvertToString),
820 EXCEPTION);
821 }
822 builder_.AppendCharacter('{');
823 Indent();
824 bool comma = false;
825 for (int i = 0; i < contents->length(); i++) {
826 Handle<String> key(String::cast(contents->get(i)), isolate_);
827 Handle<Object> property;
828 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
829 isolate_, property, Object::GetPropertyOrElement(isolate_, object, key),
830 EXCEPTION);
831 Result result = SerializeProperty(property, comma, key);
832 if (!comma && result == SUCCESS) comma = true;
833 if (result == EXCEPTION) return result;
834 }
835 Unindent();
836 if (comma) NewLine();
837 builder_.AppendCharacter('}');
838 return SUCCESS;
839 }
840
SerializeJSProxy(Handle<JSProxy> object,Handle<Object> key)841 JsonStringifier::Result JsonStringifier::SerializeJSProxy(
842 Handle<JSProxy> object, Handle<Object> key) {
843 HandleScope scope(isolate_);
844 Result stack_push = StackPush(object, key);
845 if (stack_push != SUCCESS) return stack_push;
846 Maybe<bool> is_array = Object::IsArray(object);
847 if (is_array.IsNothing()) return EXCEPTION;
848 if (is_array.FromJust()) {
849 Handle<Object> length_object;
850 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
851 isolate_, length_object,
852 Object::GetLengthFromArrayLike(isolate_,
853 Handle<JSReceiver>::cast(object)),
854 EXCEPTION);
855 uint32_t length;
856 if (!length_object->ToUint32(&length)) {
857 // Technically, we need to be able to handle lengths outside the
858 // uint32_t range. However, we would run into string size overflow
859 // if we tried to stringify such an array.
860 isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError());
861 return EXCEPTION;
862 }
863 builder_.AppendCharacter('[');
864 Indent();
865 Result result = SerializeArrayLikeSlow(object, 0, length);
866 if (result != SUCCESS) return result;
867 Unindent();
868 if (length > 0) NewLine();
869 builder_.AppendCharacter(']');
870 } else {
871 Result result = SerializeJSReceiverSlow(object);
872 if (result != SUCCESS) return result;
873 }
874 StackPop();
875 return SUCCESS;
876 }
877
878 template <typename SrcChar, typename DestChar>
SerializeStringUnchecked_(base::Vector<const SrcChar> src,IncrementalStringBuilder::NoExtend<DestChar> * dest)879 void JsonStringifier::SerializeStringUnchecked_(
880 base::Vector<const SrcChar> src,
881 IncrementalStringBuilder::NoExtend<DestChar>* dest) {
882 // Assert that base::uc16 character is not truncated down to 8 bit.
883 // The <base::uc16, char> version of this method must not be called.
884 DCHECK(sizeof(DestChar) >= sizeof(SrcChar));
885 for (int i = 0; i < src.length(); i++) {
886 SrcChar c = src[i];
887 if (DoNotEscape(c)) {
888 dest->Append(c);
889 } else if (c >= 0xD800 && c <= 0xDFFF) {
890 // The current character is a surrogate.
891 if (c <= 0xDBFF) {
892 // The current character is a leading surrogate.
893 if (i + 1 < src.length()) {
894 // There is a next character.
895 SrcChar next = src[i + 1];
896 if (next >= 0xDC00 && next <= 0xDFFF) {
897 // The next character is a trailing surrogate, meaning this is a
898 // surrogate pair.
899 dest->Append(c);
900 dest->Append(next);
901 i++;
902 } else {
903 // The next character is not a trailing surrogate. Thus, the
904 // current character is a lone leading surrogate.
905 dest->AppendCString("\\u");
906 char* const hex = DoubleToRadixCString(c, 16);
907 dest->AppendCString(hex);
908 DeleteArray(hex);
909 }
910 } else {
911 // There is no next character. Thus, the current character is a lone
912 // leading surrogate.
913 dest->AppendCString("\\u");
914 char* const hex = DoubleToRadixCString(c, 16);
915 dest->AppendCString(hex);
916 DeleteArray(hex);
917 }
918 } else {
919 // The current character is a lone trailing surrogate. (If it had been
920 // preceded by a leading surrogate, we would've ended up in the other
921 // branch earlier on, and the current character would've been handled
922 // as part of the surrogate pair already.)
923 dest->AppendCString("\\u");
924 char* const hex = DoubleToRadixCString(c, 16);
925 dest->AppendCString(hex);
926 DeleteArray(hex);
927 }
928 } else {
929 dest->AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
930 }
931 }
932 }
933
934 template <typename SrcChar, typename DestChar>
SerializeString_(Handle<String> string)935 void JsonStringifier::SerializeString_(Handle<String> string) {
936 int length = string->length();
937 builder_.Append<uint8_t, DestChar>('"');
938 // We might be able to fit the whole escaped string in the current string
939 // part, or we might need to allocate.
940 if (int worst_case_length = builder_.EscapedLengthIfCurrentPartFits(length)) {
941 DisallowGarbageCollection no_gc;
942 base::Vector<const SrcChar> vector = string->GetCharVector<SrcChar>(no_gc);
943 IncrementalStringBuilder::NoExtendBuilder<DestChar> no_extend(
944 &builder_, worst_case_length, no_gc);
945 SerializeStringUnchecked_(vector, &no_extend);
946 } else {
947 FlatStringReader reader(isolate_, string);
948 for (int i = 0; i < reader.length(); i++) {
949 SrcChar c = reader.Get<SrcChar>(i);
950 if (DoNotEscape(c)) {
951 builder_.Append<SrcChar, DestChar>(c);
952 } else if (c >= 0xD800 && c <= 0xDFFF) {
953 // The current character is a surrogate.
954 if (c <= 0xDBFF) {
955 // The current character is a leading surrogate.
956 if (i + 1 < reader.length()) {
957 // There is a next character.
958 SrcChar next = reader.Get<SrcChar>(i + 1);
959 if (next >= 0xDC00 && next <= 0xDFFF) {
960 // The next character is a trailing surrogate, meaning this is a
961 // surrogate pair.
962 builder_.Append<SrcChar, DestChar>(c);
963 builder_.Append<SrcChar, DestChar>(next);
964 i++;
965 } else {
966 // The next character is not a trailing surrogate. Thus, the
967 // current character is a lone leading surrogate.
968 builder_.AppendCString("\\u");
969 char* const hex = DoubleToRadixCString(c, 16);
970 builder_.AppendCString(hex);
971 DeleteArray(hex);
972 }
973 } else {
974 // There is no next character. Thus, the current character is a
975 // lone leading surrogate.
976 builder_.AppendCString("\\u");
977 char* const hex = DoubleToRadixCString(c, 16);
978 builder_.AppendCString(hex);
979 DeleteArray(hex);
980 }
981 } else {
982 // The current character is a lone trailing surrogate. (If it had
983 // been preceded by a leading surrogate, we would've ended up in the
984 // other branch earlier on, and the current character would've been
985 // handled as part of the surrogate pair already.)
986 builder_.AppendCString("\\u");
987 char* const hex = DoubleToRadixCString(c, 16);
988 builder_.AppendCString(hex);
989 DeleteArray(hex);
990 }
991 } else {
992 builder_.AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
993 }
994 }
995 }
996 builder_.Append<uint8_t, DestChar>('"');
997 }
998
999 template <>
DoNotEscape(uint8_t c)1000 bool JsonStringifier::DoNotEscape(uint8_t c) {
1001 // https://tc39.github.io/ecma262/#table-json-single-character-escapes
1002 return c >= 0x23 && c <= 0x7E && c != 0x5C;
1003 }
1004
1005 template <>
DoNotEscape(uint16_t c)1006 bool JsonStringifier::DoNotEscape(uint16_t c) {
1007 // https://tc39.github.io/ecma262/#table-json-single-character-escapes
1008 return c >= 0x23 && c != 0x5C && c != 0x7F && (c < 0xD800 || c > 0xDFFF);
1009 }
1010
NewLine()1011 void JsonStringifier::NewLine() {
1012 if (gap_ == nullptr) return;
1013 builder_.AppendCharacter('\n');
1014 for (int i = 0; i < indent_; i++) builder_.AppendCString(gap_);
1015 }
1016
Separator(bool first)1017 void JsonStringifier::Separator(bool first) {
1018 if (!first) builder_.AppendCharacter(',');
1019 NewLine();
1020 }
1021
SerializeDeferredKey(bool deferred_comma,Handle<Object> deferred_key)1022 void JsonStringifier::SerializeDeferredKey(bool deferred_comma,
1023 Handle<Object> deferred_key) {
1024 Separator(!deferred_comma);
1025 SerializeString(Handle<String>::cast(deferred_key));
1026 builder_.AppendCharacter(':');
1027 if (gap_ != nullptr) builder_.AppendCharacter(' ');
1028 }
1029
SerializeString(Handle<String> object)1030 void JsonStringifier::SerializeString(Handle<String> object) {
1031 object = String::Flatten(isolate_, object);
1032 if (builder_.CurrentEncoding() == String::ONE_BYTE_ENCODING) {
1033 if (String::IsOneByteRepresentationUnderneath(*object)) {
1034 SerializeString_<uint8_t, uint8_t>(object);
1035 } else {
1036 builder_.ChangeEncoding();
1037 SerializeString(object);
1038 }
1039 } else {
1040 if (String::IsOneByteRepresentationUnderneath(*object)) {
1041 SerializeString_<uint8_t, base::uc16>(object);
1042 } else {
1043 SerializeString_<base::uc16, base::uc16>(object);
1044 }
1045 }
1046 }
1047
1048 } // namespace internal
1049 } // namespace v8
1050