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/lookup.h"
9 #include "src/messages.h"
10 #include "src/objects-inl.h"
11 #include "src/utils.h"
12
13 namespace v8 {
14 namespace internal {
15
16 // Translation table to escape Latin1 characters.
17 // Table entries start at a multiple of 8 and are null-terminated.
18 const char* const JsonStringifier::JsonEscapeTable =
19 "\\u0000\0 \\u0001\0 \\u0002\0 \\u0003\0 "
20 "\\u0004\0 \\u0005\0 \\u0006\0 \\u0007\0 "
21 "\\b\0 \\t\0 \\n\0 \\u000b\0 "
22 "\\f\0 \\r\0 \\u000e\0 \\u000f\0 "
23 "\\u0010\0 \\u0011\0 \\u0012\0 \\u0013\0 "
24 "\\u0014\0 \\u0015\0 \\u0016\0 \\u0017\0 "
25 "\\u0018\0 \\u0019\0 \\u001a\0 \\u001b\0 "
26 "\\u001c\0 \\u001d\0 \\u001e\0 \\u001f\0 "
27 " \0 !\0 \\\"\0 #\0 "
28 "$\0 %\0 &\0 '\0 "
29 "(\0 )\0 *\0 +\0 "
30 ",\0 -\0 .\0 /\0 "
31 "0\0 1\0 2\0 3\0 "
32 "4\0 5\0 6\0 7\0 "
33 "8\0 9\0 :\0 ;\0 "
34 "<\0 =\0 >\0 ?\0 "
35 "@\0 A\0 B\0 C\0 "
36 "D\0 E\0 F\0 G\0 "
37 "H\0 I\0 J\0 K\0 "
38 "L\0 M\0 N\0 O\0 "
39 "P\0 Q\0 R\0 S\0 "
40 "T\0 U\0 V\0 W\0 "
41 "X\0 Y\0 Z\0 [\0 "
42 "\\\\\0 ]\0 ^\0 _\0 "
43 "`\0 a\0 b\0 c\0 "
44 "d\0 e\0 f\0 g\0 "
45 "h\0 i\0 j\0 k\0 "
46 "l\0 m\0 n\0 o\0 "
47 "p\0 q\0 r\0 s\0 "
48 "t\0 u\0 v\0 w\0 "
49 "x\0 y\0 z\0 {\0 "
50 "|\0 }\0 ~\0 \177\0 "
51 "\200\0 \201\0 \202\0 \203\0 "
52 "\204\0 \205\0 \206\0 \207\0 "
53 "\210\0 \211\0 \212\0 \213\0 "
54 "\214\0 \215\0 \216\0 \217\0 "
55 "\220\0 \221\0 \222\0 \223\0 "
56 "\224\0 \225\0 \226\0 \227\0 "
57 "\230\0 \231\0 \232\0 \233\0 "
58 "\234\0 \235\0 \236\0 \237\0 "
59 "\240\0 \241\0 \242\0 \243\0 "
60 "\244\0 \245\0 \246\0 \247\0 "
61 "\250\0 \251\0 \252\0 \253\0 "
62 "\254\0 \255\0 \256\0 \257\0 "
63 "\260\0 \261\0 \262\0 \263\0 "
64 "\264\0 \265\0 \266\0 \267\0 "
65 "\270\0 \271\0 \272\0 \273\0 "
66 "\274\0 \275\0 \276\0 \277\0 "
67 "\300\0 \301\0 \302\0 \303\0 "
68 "\304\0 \305\0 \306\0 \307\0 "
69 "\310\0 \311\0 \312\0 \313\0 "
70 "\314\0 \315\0 \316\0 \317\0 "
71 "\320\0 \321\0 \322\0 \323\0 "
72 "\324\0 \325\0 \326\0 \327\0 "
73 "\330\0 \331\0 \332\0 \333\0 "
74 "\334\0 \335\0 \336\0 \337\0 "
75 "\340\0 \341\0 \342\0 \343\0 "
76 "\344\0 \345\0 \346\0 \347\0 "
77 "\350\0 \351\0 \352\0 \353\0 "
78 "\354\0 \355\0 \356\0 \357\0 "
79 "\360\0 \361\0 \362\0 \363\0 "
80 "\364\0 \365\0 \366\0 \367\0 "
81 "\370\0 \371\0 \372\0 \373\0 "
82 "\374\0 \375\0 \376\0 \377\0 ";
83
JsonStringifier(Isolate * isolate)84 JsonStringifier::JsonStringifier(Isolate* isolate)
85 : isolate_(isolate), builder_(isolate), gap_(nullptr), indent_(0) {
86 tojson_string_ = factory()->toJSON_string();
87 stack_ = factory()->NewJSArray(8);
88 }
89
Stringify(Handle<Object> object,Handle<Object> replacer,Handle<Object> gap)90 MaybeHandle<Object> JsonStringifier::Stringify(Handle<Object> object,
91 Handle<Object> replacer,
92 Handle<Object> gap) {
93 if (!InitializeReplacer(replacer)) return MaybeHandle<Object>();
94 if (!gap->IsUndefined(isolate_) && !InitializeGap(gap)) {
95 return MaybeHandle<Object>();
96 }
97 Result result = SerializeObject(object);
98 if (result == UNCHANGED) return factory()->undefined_value();
99 if (result == SUCCESS) return builder_.Finish();
100 DCHECK(result == EXCEPTION);
101 return MaybeHandle<Object>();
102 }
103
IsInList(Handle<String> key,List<Handle<String>> * list)104 bool IsInList(Handle<String> key, List<Handle<String> >* list) {
105 // TODO(yangguo): This is O(n^2) for n properties in the list. Deal with this
106 // if this becomes an issue.
107 for (const Handle<String>& existing : *list) {
108 if (String::Equals(existing, key)) return true;
109 }
110 return false;
111 }
112
InitializeReplacer(Handle<Object> replacer)113 bool JsonStringifier::InitializeReplacer(Handle<Object> replacer) {
114 DCHECK(property_list_.is_null());
115 DCHECK(replacer_function_.is_null());
116 Maybe<bool> is_array = Object::IsArray(replacer);
117 if (is_array.IsNothing()) return false;
118 if (is_array.FromJust()) {
119 HandleScope handle_scope(isolate_);
120 List<Handle<String> > list;
121 Handle<Object> length_obj;
122 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
123 isolate_, length_obj,
124 Object::GetLengthFromArrayLike(isolate_, replacer), false);
125 uint32_t length;
126 if (!length_obj->ToUint32(&length)) length = kMaxUInt32;
127 for (uint32_t i = 0; i < length; i++) {
128 Handle<Object> element;
129 Handle<String> key;
130 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
131 isolate_, element, Object::GetElement(isolate_, replacer, i), false);
132 if (element->IsNumber() || element->IsString()) {
133 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
134 isolate_, key, Object::ToString(isolate_, element), false);
135 } else if (element->IsJSValue()) {
136 Handle<Object> value(Handle<JSValue>::cast(element)->value(), isolate_);
137 if (value->IsNumber() || value->IsString()) {
138 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
139 isolate_, key, Object::ToString(isolate_, element), false);
140 }
141 }
142 if (key.is_null()) continue;
143 if (!IsInList(key, &list)) list.Add(key);
144 }
145 property_list_ = factory()->NewUninitializedFixedArray(list.length());
146 for (int i = 0; i < list.length(); i++) {
147 property_list_->set(i, *list[i]);
148 }
149 property_list_ = handle_scope.CloseAndEscape(property_list_);
150 } else if (replacer->IsCallable()) {
151 replacer_function_ = Handle<JSReceiver>::cast(replacer);
152 }
153 return true;
154 }
155
InitializeGap(Handle<Object> gap)156 bool JsonStringifier::InitializeGap(Handle<Object> gap) {
157 DCHECK_NULL(gap_);
158 HandleScope scope(isolate_);
159 if (gap->IsJSValue()) {
160 Handle<Object> value(Handle<JSValue>::cast(gap)->value(), isolate_);
161 if (value->IsString()) {
162 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap,
163 Object::ToString(isolate_, gap), false);
164 } else if (value->IsNumber()) {
165 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap, Object::ToNumber(gap),
166 false);
167 }
168 }
169
170 if (gap->IsString()) {
171 Handle<String> gap_string = Handle<String>::cast(gap);
172 if (gap_string->length() > 0) {
173 int gap_length = std::min(gap_string->length(), 10);
174 gap_ = NewArray<uc16>(gap_length + 1);
175 String::WriteToFlat(*gap_string, gap_, 0, gap_length);
176 for (int i = 0; i < gap_length; i++) {
177 if (gap_[i] > String::kMaxOneByteCharCode) {
178 builder_.ChangeEncoding();
179 break;
180 }
181 }
182 gap_[gap_length] = '\0';
183 }
184 } else if (gap->IsNumber()) {
185 int num_value = DoubleToInt32(gap->Number());
186 if (num_value > 0) {
187 int gap_length = std::min(num_value, 10);
188 gap_ = NewArray<uc16>(gap_length + 1);
189 for (int i = 0; i < gap_length; i++) gap_[i] = ' ';
190 gap_[gap_length] = '\0';
191 }
192 }
193 return true;
194 }
195
ApplyToJsonFunction(Handle<Object> object,Handle<Object> key)196 MaybeHandle<Object> JsonStringifier::ApplyToJsonFunction(Handle<Object> object,
197 Handle<Object> key) {
198 HandleScope scope(isolate_);
199 LookupIterator it(object, tojson_string_,
200 LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
201 Handle<Object> fun;
202 ASSIGN_RETURN_ON_EXCEPTION(isolate_, fun, Object::GetProperty(&it), Object);
203 if (!fun->IsCallable()) return object;
204
205 // Call toJSON function.
206 if (key->IsSmi()) key = factory()->NumberToString(key);
207 Handle<Object> argv[] = {key};
208 ASSIGN_RETURN_ON_EXCEPTION(isolate_, object,
209 Execution::Call(isolate_, fun, object, 1, argv),
210 Object);
211 return scope.CloseAndEscape(object);
212 }
213
ApplyReplacerFunction(Handle<Object> value,Handle<Object> key,Handle<Object> initial_holder)214 MaybeHandle<Object> JsonStringifier::ApplyReplacerFunction(
215 Handle<Object> value, Handle<Object> key, Handle<Object> initial_holder) {
216 HandleScope scope(isolate_);
217 if (key->IsSmi()) key = factory()->NumberToString(key);
218 Handle<Object> argv[] = {key, value};
219 Handle<JSReceiver> holder = CurrentHolder(value, initial_holder);
220 ASSIGN_RETURN_ON_EXCEPTION(
221 isolate_, value,
222 Execution::Call(isolate_, replacer_function_, holder, 2, argv), Object);
223 return scope.CloseAndEscape(value);
224 }
225
CurrentHolder(Handle<Object> value,Handle<Object> initial_holder)226 Handle<JSReceiver> JsonStringifier::CurrentHolder(
227 Handle<Object> value, Handle<Object> initial_holder) {
228 int length = Smi::cast(stack_->length())->value();
229 if (length == 0) {
230 Handle<JSObject> holder =
231 factory()->NewJSObject(isolate_->object_function());
232 JSObject::AddProperty(holder, factory()->empty_string(), initial_holder,
233 NONE);
234 return holder;
235 } else {
236 FixedArray* elements = FixedArray::cast(stack_->elements());
237 return Handle<JSReceiver>(JSReceiver::cast(elements->get(length - 1)),
238 isolate_);
239 }
240 }
241
StackPush(Handle<Object> object)242 JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object) {
243 StackLimitCheck check(isolate_);
244 if (check.HasOverflowed()) {
245 isolate_->StackOverflow();
246 return EXCEPTION;
247 }
248
249 int length = Smi::cast(stack_->length())->value();
250 {
251 DisallowHeapAllocation no_allocation;
252 FixedArray* elements = FixedArray::cast(stack_->elements());
253 for (int i = 0; i < length; i++) {
254 if (elements->get(i) == *object) {
255 AllowHeapAllocation allow_to_return_error;
256 Handle<Object> error =
257 factory()->NewTypeError(MessageTemplate::kCircularStructure);
258 isolate_->Throw(*error);
259 return EXCEPTION;
260 }
261 }
262 }
263 JSArray::SetLength(stack_, length + 1);
264 FixedArray::cast(stack_->elements())->set(length, *object);
265 return SUCCESS;
266 }
267
StackPop()268 void JsonStringifier::StackPop() {
269 int length = Smi::cast(stack_->length())->value();
270 stack_->set_length(Smi::FromInt(length - 1));
271 }
272
273 template <bool deferred_string_key>
Serialize_(Handle<Object> object,bool comma,Handle<Object> key)274 JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
275 bool comma,
276 Handle<Object> key) {
277 StackLimitCheck interrupt_check(isolate_);
278 Handle<Object> initial_value = object;
279 if (interrupt_check.InterruptRequested() &&
280 isolate_->stack_guard()->HandleInterrupts()->IsException(isolate_)) {
281 return EXCEPTION;
282 }
283 if (object->IsJSReceiver()) {
284 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
285 isolate_, object, ApplyToJsonFunction(object, key), EXCEPTION);
286 }
287 if (!replacer_function_.is_null()) {
288 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
289 isolate_, object, ApplyReplacerFunction(object, key, initial_value),
290 EXCEPTION);
291 }
292
293 if (object->IsSmi()) {
294 if (deferred_string_key) SerializeDeferredKey(comma, key);
295 return SerializeSmi(Smi::cast(*object));
296 }
297
298 switch (HeapObject::cast(*object)->map()->instance_type()) {
299 case HEAP_NUMBER_TYPE:
300 case MUTABLE_HEAP_NUMBER_TYPE:
301 if (deferred_string_key) SerializeDeferredKey(comma, key);
302 return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
303 case ODDBALL_TYPE:
304 switch (Oddball::cast(*object)->kind()) {
305 case Oddball::kFalse:
306 if (deferred_string_key) SerializeDeferredKey(comma, key);
307 builder_.AppendCString("false");
308 return SUCCESS;
309 case Oddball::kTrue:
310 if (deferred_string_key) SerializeDeferredKey(comma, key);
311 builder_.AppendCString("true");
312 return SUCCESS;
313 case Oddball::kNull:
314 if (deferred_string_key) SerializeDeferredKey(comma, key);
315 builder_.AppendCString("null");
316 return SUCCESS;
317 default:
318 return UNCHANGED;
319 }
320 case JS_ARRAY_TYPE:
321 if (deferred_string_key) SerializeDeferredKey(comma, key);
322 return SerializeJSArray(Handle<JSArray>::cast(object));
323 case JS_VALUE_TYPE:
324 if (deferred_string_key) SerializeDeferredKey(comma, key);
325 return SerializeJSValue(Handle<JSValue>::cast(object));
326 case SYMBOL_TYPE:
327 return UNCHANGED;
328 default:
329 if (object->IsString()) {
330 if (deferred_string_key) SerializeDeferredKey(comma, key);
331 SerializeString(Handle<String>::cast(object));
332 return SUCCESS;
333 } else {
334 DCHECK(object->IsJSReceiver());
335 if (object->IsCallable()) return UNCHANGED;
336 // Go to slow path for global proxy and objects requiring access checks.
337 if (deferred_string_key) SerializeDeferredKey(comma, key);
338 if (object->IsJSProxy()) {
339 return SerializeJSProxy(Handle<JSProxy>::cast(object));
340 }
341 return SerializeJSObject(Handle<JSObject>::cast(object));
342 }
343 }
344
345 UNREACHABLE();
346 return UNCHANGED;
347 }
348
SerializeJSValue(Handle<JSValue> object)349 JsonStringifier::Result JsonStringifier::SerializeJSValue(
350 Handle<JSValue> object) {
351 String* class_name = object->class_name();
352 if (class_name == isolate_->heap()->String_string()) {
353 Handle<Object> value;
354 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
355 isolate_, value, Object::ToString(isolate_, object), EXCEPTION);
356 SerializeString(Handle<String>::cast(value));
357 } else if (class_name == isolate_->heap()->Number_string()) {
358 Handle<Object> value;
359 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, value, Object::ToNumber(object),
360 EXCEPTION);
361 if (value->IsSmi()) return SerializeSmi(Smi::cast(*value));
362 SerializeHeapNumber(Handle<HeapNumber>::cast(value));
363 } else if (class_name == isolate_->heap()->Boolean_string()) {
364 Object* value = JSValue::cast(*object)->value();
365 DCHECK(value->IsBoolean());
366 builder_.AppendCString(value->IsTrue(isolate_) ? "true" : "false");
367 } else {
368 // ES6 24.3.2.1 step 10.c, serialize as an ordinary JSObject.
369 return SerializeJSObject(object);
370 }
371 return SUCCESS;
372 }
373
SerializeSmi(Smi * object)374 JsonStringifier::Result JsonStringifier::SerializeSmi(Smi* object) {
375 static const int kBufferSize = 100;
376 char chars[kBufferSize];
377 Vector<char> buffer(chars, kBufferSize);
378 builder_.AppendCString(IntToCString(object->value(), buffer));
379 return SUCCESS;
380 }
381
SerializeDouble(double number)382 JsonStringifier::Result JsonStringifier::SerializeDouble(double number) {
383 if (std::isinf(number) || std::isnan(number)) {
384 builder_.AppendCString("null");
385 return SUCCESS;
386 }
387 static const int kBufferSize = 100;
388 char chars[kBufferSize];
389 Vector<char> buffer(chars, kBufferSize);
390 builder_.AppendCString(DoubleToCString(number, buffer));
391 return SUCCESS;
392 }
393
SerializeJSArray(Handle<JSArray> object)394 JsonStringifier::Result JsonStringifier::SerializeJSArray(
395 Handle<JSArray> object) {
396 HandleScope handle_scope(isolate_);
397 Result stack_push = StackPush(object);
398 if (stack_push != SUCCESS) return stack_push;
399 uint32_t length = 0;
400 CHECK(object->length()->ToArrayLength(&length));
401 DCHECK(!object->IsAccessCheckNeeded());
402 builder_.AppendCharacter('[');
403 Indent();
404 uint32_t i = 0;
405 if (replacer_function_.is_null()) {
406 switch (object->GetElementsKind()) {
407 case FAST_SMI_ELEMENTS: {
408 Handle<FixedArray> elements(FixedArray::cast(object->elements()),
409 isolate_);
410 StackLimitCheck interrupt_check(isolate_);
411 while (i < length) {
412 if (interrupt_check.InterruptRequested() &&
413 isolate_->stack_guard()->HandleInterrupts()->IsException(
414 isolate_)) {
415 return EXCEPTION;
416 }
417 Separator(i == 0);
418 SerializeSmi(Smi::cast(elements->get(i)));
419 i++;
420 }
421 break;
422 }
423 case FAST_DOUBLE_ELEMENTS: {
424 // Empty array is FixedArray but not FixedDoubleArray.
425 if (length == 0) break;
426 Handle<FixedDoubleArray> elements(
427 FixedDoubleArray::cast(object->elements()), isolate_);
428 StackLimitCheck interrupt_check(isolate_);
429 while (i < length) {
430 if (interrupt_check.InterruptRequested() &&
431 isolate_->stack_guard()->HandleInterrupts()->IsException(
432 isolate_)) {
433 return EXCEPTION;
434 }
435 Separator(i == 0);
436 SerializeDouble(elements->get_scalar(i));
437 i++;
438 }
439 break;
440 }
441 case FAST_ELEMENTS: {
442 Handle<Object> old_length(object->length(), isolate_);
443 while (i < length) {
444 if (object->length() != *old_length ||
445 object->GetElementsKind() != FAST_ELEMENTS) {
446 // Fall back to slow path.
447 break;
448 }
449 Separator(i == 0);
450 Result result = SerializeElement(
451 isolate_,
452 Handle<Object>(FixedArray::cast(object->elements())->get(i),
453 isolate_),
454 i);
455 if (result == UNCHANGED) {
456 builder_.AppendCString("null");
457 } else if (result != SUCCESS) {
458 return result;
459 }
460 i++;
461 }
462 break;
463 }
464 // The FAST_HOLEY_* cases could be handled in a faster way. They resemble
465 // the non-holey cases except that a lookup is necessary for holes.
466 default:
467 break;
468 }
469 }
470 if (i < length) {
471 // Slow path for non-fast elements and fall-back in edge case.
472 Result result = SerializeArrayLikeSlow(object, i, length);
473 if (result != SUCCESS) return result;
474 }
475 Unindent();
476 if (length > 0) NewLine();
477 builder_.AppendCharacter(']');
478 StackPop();
479 return SUCCESS;
480 }
481
SerializeArrayLikeSlow(Handle<JSReceiver> object,uint32_t start,uint32_t length)482 JsonStringifier::Result JsonStringifier::SerializeArrayLikeSlow(
483 Handle<JSReceiver> object, uint32_t start, uint32_t length) {
484 // We need to write out at least two characters per array element.
485 static const int kMaxSerializableArrayLength = String::kMaxLength / 2;
486 if (length > kMaxSerializableArrayLength) {
487 isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError());
488 return EXCEPTION;
489 }
490 for (uint32_t i = start; i < length; i++) {
491 Separator(i == 0);
492 Handle<Object> element;
493 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
494 isolate_, element, JSReceiver::GetElement(isolate_, object, i),
495 EXCEPTION);
496 Result result = SerializeElement(isolate_, element, i);
497 if (result == SUCCESS) continue;
498 if (result == UNCHANGED) {
499 // Detect overflow sooner for large sparse arrays.
500 if (builder_.HasOverflowed()) return EXCEPTION;
501 builder_.AppendCString("null");
502 } else {
503 return result;
504 }
505 }
506 return SUCCESS;
507 }
508
SerializeJSObject(Handle<JSObject> object)509 JsonStringifier::Result JsonStringifier::SerializeJSObject(
510 Handle<JSObject> object) {
511 HandleScope handle_scope(isolate_);
512 Result stack_push = StackPush(object);
513 if (stack_push != SUCCESS) return stack_push;
514
515 if (property_list_.is_null() &&
516 object->map()->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER &&
517 object->HasFastProperties() &&
518 Handle<JSObject>::cast(object)->elements()->length() == 0) {
519 DCHECK(object->IsJSObject());
520 DCHECK(!object->IsJSGlobalProxy());
521 Handle<JSObject> js_obj = Handle<JSObject>::cast(object);
522 DCHECK(!js_obj->HasIndexedInterceptor());
523 DCHECK(!js_obj->HasNamedInterceptor());
524 Handle<Map> map(js_obj->map());
525 builder_.AppendCharacter('{');
526 Indent();
527 bool comma = false;
528 for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
529 Handle<Name> name(map->instance_descriptors()->GetKey(i), isolate_);
530 // TODO(rossberg): Should this throw?
531 if (!name->IsString()) continue;
532 Handle<String> key = Handle<String>::cast(name);
533 PropertyDetails details = map->instance_descriptors()->GetDetails(i);
534 if (details.IsDontEnum()) continue;
535 Handle<Object> property;
536 if (details.location() == kField && *map == js_obj->map()) {
537 DCHECK_EQ(kData, details.kind());
538 FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
539 property = JSObject::FastPropertyAt(js_obj, details.representation(),
540 field_index);
541 } else {
542 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
543 isolate_, property, Object::GetPropertyOrElement(js_obj, key),
544 EXCEPTION);
545 }
546 Result result = SerializeProperty(property, comma, key);
547 if (!comma && result == SUCCESS) comma = true;
548 if (result == EXCEPTION) return result;
549 }
550 Unindent();
551 if (comma) NewLine();
552 builder_.AppendCharacter('}');
553 } else {
554 Result result = SerializeJSReceiverSlow(object);
555 if (result != SUCCESS) return result;
556 }
557 StackPop();
558 return SUCCESS;
559 }
560
SerializeJSReceiverSlow(Handle<JSReceiver> object)561 JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow(
562 Handle<JSReceiver> object) {
563 Handle<FixedArray> contents = property_list_;
564 if (contents.is_null()) {
565 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
566 isolate_, contents,
567 KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
568 ENUMERABLE_STRINGS,
569 GetKeysConversion::kConvertToString),
570 EXCEPTION);
571 }
572 builder_.AppendCharacter('{');
573 Indent();
574 bool comma = false;
575 for (int i = 0; i < contents->length(); i++) {
576 Handle<String> key(String::cast(contents->get(i)), isolate_);
577 Handle<Object> property;
578 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, property,
579 Object::GetPropertyOrElement(object, key),
580 EXCEPTION);
581 Result result = SerializeProperty(property, comma, key);
582 if (!comma && result == SUCCESS) comma = true;
583 if (result == EXCEPTION) return result;
584 }
585 Unindent();
586 if (comma) NewLine();
587 builder_.AppendCharacter('}');
588 return SUCCESS;
589 }
590
SerializeJSProxy(Handle<JSProxy> object)591 JsonStringifier::Result JsonStringifier::SerializeJSProxy(
592 Handle<JSProxy> object) {
593 HandleScope scope(isolate_);
594 Result stack_push = StackPush(object);
595 if (stack_push != SUCCESS) return stack_push;
596 Maybe<bool> is_array = Object::IsArray(object);
597 if (is_array.IsNothing()) return EXCEPTION;
598 if (is_array.FromJust()) {
599 Handle<Object> length_object;
600 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
601 isolate_, length_object,
602 Object::GetLengthFromArrayLike(isolate_, object), EXCEPTION);
603 uint32_t length;
604 if (!length_object->ToUint32(&length)) {
605 // Technically, we need to be able to handle lengths outside the
606 // uint32_t range. However, we would run into string size overflow
607 // if we tried to stringify such an array.
608 isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError());
609 return EXCEPTION;
610 }
611 builder_.AppendCharacter('[');
612 Indent();
613 Result result = SerializeArrayLikeSlow(object, 0, length);
614 if (result != SUCCESS) return result;
615 Unindent();
616 if (length > 0) NewLine();
617 builder_.AppendCharacter(']');
618 } else {
619 Result result = SerializeJSReceiverSlow(object);
620 if (result != SUCCESS) return result;
621 }
622 StackPop();
623 return SUCCESS;
624 }
625
626 template <typename SrcChar, typename DestChar>
SerializeStringUnchecked_(Vector<const SrcChar> src,IncrementalStringBuilder::NoExtend<DestChar> * dest)627 void JsonStringifier::SerializeStringUnchecked_(
628 Vector<const SrcChar> src,
629 IncrementalStringBuilder::NoExtend<DestChar>* dest) {
630 // Assert that uc16 character is not truncated down to 8 bit.
631 // The <uc16, char> version of this method must not be called.
632 DCHECK(sizeof(DestChar) >= sizeof(SrcChar));
633
634 for (int i = 0; i < src.length(); i++) {
635 SrcChar c = src[i];
636 if (DoNotEscape(c)) {
637 dest->Append(c);
638 } else {
639 dest->AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
640 }
641 }
642 }
643
644 template <typename SrcChar, typename DestChar>
SerializeString_(Handle<String> string)645 void JsonStringifier::SerializeString_(Handle<String> string) {
646 int length = string->length();
647 builder_.Append<uint8_t, DestChar>('"');
648 // We make a rough estimate to find out if the current string can be
649 // serialized without allocating a new string part. The worst case length of
650 // an escaped character is 6. Shifting the remainin string length right by 3
651 // is a more pessimistic estimate, but faster to calculate.
652 int worst_case_length = length << 3;
653 if (builder_.CurrentPartCanFit(worst_case_length)) {
654 DisallowHeapAllocation no_gc;
655 Vector<const SrcChar> vector = string->GetCharVector<SrcChar>();
656 IncrementalStringBuilder::NoExtendBuilder<DestChar> no_extend(
657 &builder_, worst_case_length);
658 SerializeStringUnchecked_(vector, &no_extend);
659 } else {
660 FlatStringReader reader(isolate_, string);
661 for (int i = 0; i < reader.length(); i++) {
662 SrcChar c = reader.Get<SrcChar>(i);
663 if (DoNotEscape(c)) {
664 builder_.Append<SrcChar, DestChar>(c);
665 } else {
666 builder_.AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
667 }
668 }
669 }
670
671 builder_.Append<uint8_t, DestChar>('"');
672 }
673
674 template <>
DoNotEscape(uint8_t c)675 bool JsonStringifier::DoNotEscape(uint8_t c) {
676 return c >= '#' && c <= '~' && c != '\\';
677 }
678
679 template <>
DoNotEscape(uint16_t c)680 bool JsonStringifier::DoNotEscape(uint16_t c) {
681 return c >= '#' && c != '\\' && c != 0x7f;
682 }
683
NewLine()684 void JsonStringifier::NewLine() {
685 if (gap_ == nullptr) return;
686 builder_.AppendCharacter('\n');
687 for (int i = 0; i < indent_; i++) builder_.AppendCString(gap_);
688 }
689
Separator(bool first)690 void JsonStringifier::Separator(bool first) {
691 if (!first) builder_.AppendCharacter(',');
692 NewLine();
693 }
694
SerializeDeferredKey(bool deferred_comma,Handle<Object> deferred_key)695 void JsonStringifier::SerializeDeferredKey(bool deferred_comma,
696 Handle<Object> deferred_key) {
697 Separator(!deferred_comma);
698 SerializeString(Handle<String>::cast(deferred_key));
699 builder_.AppendCharacter(':');
700 if (gap_ != nullptr) builder_.AppendCharacter(' ');
701 }
702
SerializeString(Handle<String> object)703 void JsonStringifier::SerializeString(Handle<String> object) {
704 object = String::Flatten(object);
705 if (builder_.CurrentEncoding() == String::ONE_BYTE_ENCODING) {
706 if (object->IsOneByteRepresentationUnderneath()) {
707 SerializeString_<uint8_t, uint8_t>(object);
708 } else {
709 builder_.ChangeEncoding();
710 SerializeString(object);
711 }
712 } else {
713 if (object->IsOneByteRepresentationUnderneath()) {
714 SerializeString_<uint8_t, uc16>(object);
715 } else {
716 SerializeString_<uc16, uc16>(object);
717 }
718 }
719 }
720
721 } // namespace internal
722 } // namespace v8
723