• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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