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