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