Line data Source code
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/message-template.h"
10 : #include "src/objects-inl.h"
11 : #include "src/objects/heap-number-inl.h"
12 : #include "src/objects/js-array-inl.h"
13 : #include "src/objects/oddball-inl.h"
14 : #include "src/objects/ordered-hash-table.h"
15 : #include "src/objects/smi.h"
16 : #include "src/string-builder-inl.h"
17 : #include "src/utils.h"
18 :
19 : namespace v8 {
20 : namespace internal {
21 :
22 : class JsonStringifier {
23 : public:
24 : explicit JsonStringifier(Isolate* isolate);
25 :
26 2252276 : ~JsonStringifier() { DeleteArray(gap_); }
27 :
28 : V8_WARN_UNUSED_RESULT MaybeHandle<Object> Stringify(Handle<Object> object,
29 : Handle<Object> replacer,
30 : Handle<Object> gap);
31 :
32 : private:
33 : enum Result { UNCHANGED, SUCCESS, EXCEPTION };
34 :
35 : bool InitializeReplacer(Handle<Object> replacer);
36 : bool InitializeGap(Handle<Object> gap);
37 :
38 : V8_WARN_UNUSED_RESULT MaybeHandle<Object> ApplyToJsonFunction(
39 : Handle<Object> object, Handle<Object> key);
40 : V8_WARN_UNUSED_RESULT MaybeHandle<Object> ApplyReplacerFunction(
41 : Handle<Object> value, Handle<Object> key, Handle<Object> initial_holder);
42 :
43 : // Entry point to serialize the object.
44 : V8_INLINE Result SerializeObject(Handle<Object> obj) {
45 1126129 : return Serialize_<false>(obj, false, factory()->empty_string());
46 : }
47 :
48 : // Serialize an array element.
49 : // The index may serve as argument for the toJSON function.
50 : V8_INLINE Result SerializeElement(Isolate* isolate, Handle<Object> object,
51 : int i) {
52 10025369 : return Serialize_<false>(object, false,
53 10025369 : Handle<Object>(Smi::FromInt(i), isolate));
54 : }
55 :
56 : // Serialize a object property.
57 : // The key may or may not be serialized depending on the property.
58 : // The key may also serve as argument for the toJSON function.
59 : V8_INLINE Result SerializeProperty(Handle<Object> object, bool deferred_comma,
60 : Handle<String> deferred_key) {
61 : DCHECK(!deferred_key.is_null());
62 907768 : return Serialize_<true>(object, deferred_comma, deferred_key);
63 : }
64 :
65 : template <bool deferred_string_key>
66 : Result Serialize_(Handle<Object> object, bool comma, Handle<Object> key);
67 :
68 : V8_INLINE void SerializeDeferredKey(bool deferred_comma,
69 : Handle<Object> deferred_key);
70 :
71 : Result SerializeSmi(Smi object);
72 :
73 : Result SerializeDouble(double number);
74 : V8_INLINE Result SerializeHeapNumber(Handle<HeapNumber> object) {
75 1579 : return SerializeDouble(object->value());
76 : }
77 :
78 : Result SerializeJSValue(Handle<JSValue> object, Handle<Object> key);
79 :
80 : V8_INLINE Result SerializeJSArray(Handle<JSArray> object, Handle<Object> key);
81 : V8_INLINE Result SerializeJSObject(Handle<JSObject> object,
82 : Handle<Object> key);
83 :
84 : Result SerializeJSProxy(Handle<JSProxy> object, Handle<Object> key);
85 : Result SerializeJSReceiverSlow(Handle<JSReceiver> object);
86 : Result SerializeArrayLikeSlow(Handle<JSReceiver> object, uint32_t start,
87 : uint32_t length);
88 :
89 : void SerializeString(Handle<String> object);
90 :
91 : template <typename SrcChar, typename DestChar>
92 : V8_INLINE static void SerializeStringUnchecked_(
93 : Vector<const SrcChar> src,
94 : IncrementalStringBuilder::NoExtend<DestChar>* dest);
95 :
96 : template <typename SrcChar, typename DestChar>
97 : V8_INLINE void SerializeString_(Handle<String> string);
98 :
99 : template <typename Char>
100 : V8_INLINE static bool DoNotEscape(Char c);
101 :
102 : V8_INLINE void NewLine();
103 412915 : V8_INLINE void Indent() { indent_++; }
104 402757 : V8_INLINE void Unindent() { indent_--; }
105 : V8_INLINE void Separator(bool first);
106 :
107 : Handle<JSReceiver> CurrentHolder(Handle<Object> value,
108 : Handle<Object> inital_holder);
109 :
110 : Result StackPush(Handle<Object> object, Handle<Object> key);
111 : void StackPop();
112 :
113 : // Uses the current stack_ to provide a detailed error message of
114 : // the objects involved in the circular structure.
115 : Handle<String> ConstructCircularStructureErrorMessage(Handle<Object> last_key,
116 : size_t start_index);
117 : // The prefix and postfix count do NOT include the starting and
118 : // closing lines of the error message.
119 : static const int kCircularErrorMessagePrefixCount = 2;
120 : static const int kCircularErrorMessagePostfixCount = 1;
121 :
122 : Factory* factory() { return isolate_->factory(); }
123 :
124 : Isolate* isolate_;
125 : IncrementalStringBuilder builder_;
126 : Handle<String> tojson_string_;
127 : Handle<FixedArray> property_list_;
128 : Handle<JSReceiver> replacer_function_;
129 : uc16* gap_;
130 : int indent_;
131 :
132 : using KeyObject = std::pair<Handle<Object>, Handle<Object>>;
133 : std::vector<KeyObject> stack_;
134 :
135 : static const int kJsonEscapeTableEntrySize = 8;
136 : static const char* const JsonEscapeTable;
137 : };
138 :
139 1126138 : MaybeHandle<Object> JsonStringify(Isolate* isolate, Handle<Object> object,
140 : Handle<Object> replacer, Handle<Object> gap) {
141 1126138 : JsonStringifier stringifier(isolate);
142 2252276 : return stringifier.Stringify(object, replacer, gap);
143 : }
144 :
145 : // Translation table to escape Latin1 characters.
146 : // Table entries start at a multiple of 8 and are null-terminated.
147 : const char* const JsonStringifier::JsonEscapeTable =
148 : "\\u0000\0 \\u0001\0 \\u0002\0 \\u0003\0 "
149 : "\\u0004\0 \\u0005\0 \\u0006\0 \\u0007\0 "
150 : "\\b\0 \\t\0 \\n\0 \\u000b\0 "
151 : "\\f\0 \\r\0 \\u000e\0 \\u000f\0 "
152 : "\\u0010\0 \\u0011\0 \\u0012\0 \\u0013\0 "
153 : "\\u0014\0 \\u0015\0 \\u0016\0 \\u0017\0 "
154 : "\\u0018\0 \\u0019\0 \\u001a\0 \\u001b\0 "
155 : "\\u001c\0 \\u001d\0 \\u001e\0 \\u001f\0 "
156 : " \0 !\0 \\\"\0 #\0 "
157 : "$\0 %\0 &\0 '\0 "
158 : "(\0 )\0 *\0 +\0 "
159 : ",\0 -\0 .\0 /\0 "
160 : "0\0 1\0 2\0 3\0 "
161 : "4\0 5\0 6\0 7\0 "
162 : "8\0 9\0 :\0 ;\0 "
163 : "<\0 =\0 >\0 ?\0 "
164 : "@\0 A\0 B\0 C\0 "
165 : "D\0 E\0 F\0 G\0 "
166 : "H\0 I\0 J\0 K\0 "
167 : "L\0 M\0 N\0 O\0 "
168 : "P\0 Q\0 R\0 S\0 "
169 : "T\0 U\0 V\0 W\0 "
170 : "X\0 Y\0 Z\0 [\0 "
171 : "\\\\\0 ]\0 ^\0 _\0 "
172 : "`\0 a\0 b\0 c\0 "
173 : "d\0 e\0 f\0 g\0 "
174 : "h\0 i\0 j\0 k\0 "
175 : "l\0 m\0 n\0 o\0 "
176 : "p\0 q\0 r\0 s\0 "
177 : "t\0 u\0 v\0 w\0 "
178 : "x\0 y\0 z\0 {\0 "
179 : "|\0 }\0 ~\0 \x7F\0 "
180 : "\x80\0 \x81\0 \x82\0 \x83\0 "
181 : "\x84\0 \x85\0 \x86\0 \x87\0 "
182 : "\x88\0 \x89\0 \x8A\0 \x8B\0 "
183 : "\x8C\0 \x8D\0 \x8E\0 \x8F\0 "
184 : "\x90\0 \x91\0 \x92\0 \x93\0 "
185 : "\x94\0 \x95\0 \x96\0 \x97\0 "
186 : "\x98\0 \x99\0 \x9A\0 \x9B\0 "
187 : "\x9C\0 \x9D\0 \x9E\0 \x9F\0 "
188 : "\xA0\0 \xA1\0 \xA2\0 \xA3\0 "
189 : "\xA4\0 \xA5\0 \xA6\0 \xA7\0 "
190 : "\xA8\0 \xA9\0 \xAA\0 \xAB\0 "
191 : "\xAC\0 \xAD\0 \xAE\0 \xAF\0 "
192 : "\xB0\0 \xB1\0 \xB2\0 \xB3\0 "
193 : "\xB4\0 \xB5\0 \xB6\0 \xB7\0 "
194 : "\xB8\0 \xB9\0 \xBA\0 \xBB\0 "
195 : "\xBC\0 \xBD\0 \xBE\0 \xBF\0 "
196 : "\xC0\0 \xC1\0 \xC2\0 \xC3\0 "
197 : "\xC4\0 \xC5\0 \xC6\0 \xC7\0 "
198 : "\xC8\0 \xC9\0 \xCA\0 \xCB\0 "
199 : "\xCC\0 \xCD\0 \xCE\0 \xCF\0 "
200 : "\xD0\0 \xD1\0 \xD2\0 \xD3\0 "
201 : "\xD4\0 \xD5\0 \xD6\0 \xD7\0 "
202 : "\xD8\0 \xD9\0 \xDA\0 \xDB\0 "
203 : "\xDC\0 \xDD\0 \xDE\0 \xDF\0 "
204 : "\xE0\0 \xE1\0 \xE2\0 \xE3\0 "
205 : "\xE4\0 \xE5\0 \xE6\0 \xE7\0 "
206 : "\xE8\0 \xE9\0 \xEA\0 \xEB\0 "
207 : "\xEC\0 \xED\0 \xEE\0 \xEF\0 "
208 : "\xF0\0 \xF1\0 \xF2\0 \xF3\0 "
209 : "\xF4\0 \xF5\0 \xF6\0 \xF7\0 "
210 : "\xF8\0 \xF9\0 \xFA\0 \xFB\0 "
211 : "\xFC\0 \xFD\0 \xFE\0 \xFF\0 ";
212 :
213 0 : JsonStringifier::JsonStringifier(Isolate* isolate)
214 : : isolate_(isolate),
215 : builder_(isolate),
216 : gap_(nullptr),
217 : indent_(0),
218 2252276 : stack_() {
219 1126138 : tojson_string_ = factory()->toJSON_string();
220 0 : }
221 :
222 1126138 : MaybeHandle<Object> JsonStringifier::Stringify(Handle<Object> object,
223 : Handle<Object> replacer,
224 : Handle<Object> gap) {
225 1126138 : if (!InitializeReplacer(replacer)) return MaybeHandle<Object>();
226 2252258 : if (!gap->IsUndefined(isolate_) && !InitializeGap(gap)) {
227 0 : return MaybeHandle<Object>();
228 : }
229 : Result result = SerializeObject(object);
230 1126393 : if (result == UNCHANGED) return factory()->undefined_value();
231 1125865 : if (result == SUCCESS) return builder_.Finish();
232 : DCHECK(result == EXCEPTION);
233 299 : return MaybeHandle<Object>();
234 : }
235 :
236 1126138 : bool JsonStringifier::InitializeReplacer(Handle<Object> replacer) {
237 : DCHECK(property_list_.is_null());
238 : DCHECK(replacer_function_.is_null());
239 : Maybe<bool> is_array = Object::IsArray(replacer);
240 1126138 : if (is_array.IsNothing()) return false;
241 1126138 : if (is_array.FromJust()) {
242 288 : HandleScope handle_scope(isolate_);
243 288 : Handle<OrderedHashSet> set = factory()->NewOrderedHashSet();
244 : Handle<Object> length_obj;
245 576 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
246 : isolate_, length_obj,
247 : Object::GetLengthFromArrayLike(isolate_,
248 : Handle<JSReceiver>::cast(replacer)),
249 : false);
250 : uint32_t length;
251 279 : if (!length_obj->ToUint32(&length)) length = kMaxUInt32;
252 2133 : for (uint32_t i = 0; i < length; i++) {
253 : Handle<Object> element;
254 : Handle<String> key;
255 1854 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
256 : isolate_, element, Object::GetElement(isolate_, replacer, i), false);
257 1683 : if (element->IsNumber() || element->IsString()) {
258 936 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
259 : isolate_, key, Object::ToString(isolate_, element), false);
260 459 : } else if (element->IsJSValue()) {
261 27 : Handle<Object> value(Handle<JSValue>::cast(element)->value(), isolate_);
262 36 : if (value->IsNumber() || value->IsString()) {
263 54 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
264 : isolate_, key, Object::ToString(isolate_, element), false);
265 : }
266 : }
267 927 : if (key.is_null()) continue;
268 : // Object keys are internalized, so do it here.
269 495 : key = factory()->InternalizeString(key);
270 495 : set = OrderedHashSet::Add(isolate_, set, key);
271 : }
272 : property_list_ = OrderedHashSet::ConvertToKeysArray(
273 279 : isolate_, set, GetKeysConversion::kKeepNumbers);
274 279 : property_list_ = handle_scope.CloseAndEscape(property_list_);
275 1125850 : } else if (replacer->IsCallable()) {
276 7109 : replacer_function_ = Handle<JSReceiver>::cast(replacer);
277 : }
278 : return true;
279 : }
280 :
281 181716 : bool JsonStringifier::InitializeGap(Handle<Object> gap) {
282 : DCHECK_NULL(gap_);
283 181716 : HandleScope scope(isolate_);
284 181716 : if (gap->IsJSValue()) {
285 27 : Handle<Object> value(Handle<JSValue>::cast(gap)->value(), isolate_);
286 27 : if (value->IsString()) {
287 36 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap,
288 : Object::ToString(isolate_, gap), false);
289 9 : } else if (value->IsNumber()) {
290 18 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap,
291 : Object::ToNumber(isolate_, gap), false);
292 : }
293 : }
294 :
295 181716 : if (gap->IsString()) {
296 : Handle<String> gap_string = Handle<String>::cast(gap);
297 1489 : if (gap_string->length() > 0) {
298 2754 : int gap_length = std::min(gap_string->length(), 10);
299 1377 : gap_ = NewArray<uc16>(gap_length + 1);
300 1377 : String::WriteToFlat(*gap_string, gap_, 0, gap_length);
301 4305 : for (int i = 0; i < gap_length; i++) {
302 1473 : if (gap_[i] > String::kMaxOneByteCharCode) {
303 9 : builder_.ChangeEncoding();
304 : break;
305 : }
306 : }
307 1377 : gap_[gap_length] = '\0';
308 : }
309 180227 : } else if (gap->IsNumber()) {
310 180218 : int num_value = DoubleToInt32(gap->Number());
311 180218 : if (num_value > 0) {
312 202 : int gap_length = std::min(num_value, 10);
313 101 : gap_ = NewArray<uc16>(gap_length + 1);
314 413 : for (int i = 0; i < gap_length; i++) gap_[i] = ' ';
315 101 : gap_[gap_length] = '\0';
316 : }
317 : }
318 : return true;
319 : }
320 :
321 416638 : MaybeHandle<Object> JsonStringifier::ApplyToJsonFunction(Handle<Object> object,
322 : Handle<Object> key) {
323 416638 : HandleScope scope(isolate_);
324 :
325 416638 : Handle<Object> object_for_lookup = object;
326 416638 : if (object->IsBigInt()) {
327 126 : ASSIGN_RETURN_ON_EXCEPTION(isolate_, object_for_lookup,
328 : Object::ToObject(isolate_, object), Object);
329 : }
330 : DCHECK(object_for_lookup->IsJSReceiver());
331 :
332 : // Retrieve toJSON function.
333 : Handle<Object> fun;
334 : {
335 : LookupIterator it(isolate_, object_for_lookup, tojson_string_,
336 416638 : LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR);
337 833276 : ASSIGN_RETURN_ON_EXCEPTION(isolate_, fun, Object::GetProperty(&it), Object);
338 416594 : if (!fun->IsCallable()) return object;
339 : }
340 :
341 : // Call toJSON function.
342 837 : if (key->IsSmi()) key = factory()->NumberToString(key);
343 837 : Handle<Object> argv[] = {key};
344 1674 : ASSIGN_RETURN_ON_EXCEPTION(isolate_, object,
345 : Execution::Call(isolate_, fun, object, 1, argv),
346 : Object);
347 819 : return scope.CloseAndEscape(object);
348 : }
349 :
350 4969585 : MaybeHandle<Object> JsonStringifier::ApplyReplacerFunction(
351 : Handle<Object> value, Handle<Object> key, Handle<Object> initial_holder) {
352 4969585 : HandleScope scope(isolate_);
353 4969585 : if (key->IsSmi()) key = factory()->NumberToString(key);
354 4969585 : Handle<Object> argv[] = {key, value};
355 4969585 : Handle<JSReceiver> holder = CurrentHolder(value, initial_holder);
356 9939170 : ASSIGN_RETURN_ON_EXCEPTION(
357 : isolate_, value,
358 : Execution::Call(isolate_, replacer_function_, holder, 2, argv), Object);
359 4969585 : return scope.CloseAndEscape(value);
360 : }
361 :
362 4969585 : Handle<JSReceiver> JsonStringifier::CurrentHolder(
363 : Handle<Object> value, Handle<Object> initial_holder) {
364 4969585 : if (stack_.empty()) {
365 : Handle<JSObject> holder =
366 7109 : factory()->NewJSObject(isolate_->object_function());
367 7109 : JSObject::AddProperty(isolate_, holder, factory()->empty_string(),
368 7109 : initial_holder, NONE);
369 7109 : return holder;
370 : } else {
371 : return Handle<JSReceiver>(JSReceiver::cast(*stack_.back().second),
372 9924952 : isolate_);
373 : }
374 : }
375 :
376 413048 : JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object,
377 : Handle<Object> key) {
378 413048 : StackLimitCheck check(isolate_);
379 413048 : if (check.HasOverflowed()) {
380 18 : isolate_->StackOverflow();
381 18 : return EXCEPTION;
382 : }
383 :
384 : {
385 : DisallowHeapAllocation no_allocation;
386 6705876 : for (size_t i = 0; i < stack_.size(); ++i) {
387 3146502 : if (*stack_[i].second == *object) {
388 : AllowHeapAllocation allow_to_return_error;
389 : Handle<String> circle_description =
390 79 : ConstructCircularStructureErrorMessage(key, i);
391 : Handle<Object> error = factory()->NewTypeError(
392 79 : MessageTemplate::kCircularStructure, circle_description);
393 79 : isolate_->Throw(*error);
394 : return EXCEPTION;
395 : }
396 : }
397 : }
398 412951 : stack_.emplace_back(key, object);
399 412951 : return SUCCESS;
400 : }
401 :
402 798592 : void JsonStringifier::StackPop() { stack_.pop_back(); }
403 :
404 : class CircularStructureMessageBuilder {
405 : public:
406 : explicit CircularStructureMessageBuilder(Isolate* isolate)
407 79 : : builder_(isolate) {}
408 :
409 79 : void AppendStartLine(Handle<Object> start_object) {
410 79 : builder_.AppendCString(kStartPrefix);
411 : builder_.AppendCString("starting at object with constructor ");
412 79 : AppendConstructorName(start_object);
413 79 : }
414 :
415 45 : void AppendNormalLine(Handle<Object> key, Handle<Object> object) {
416 45 : builder_.AppendCString(kLinePrefix);
417 45 : AppendKey(key);
418 : builder_.AppendCString(" -> object with constructor ");
419 45 : AppendConstructorName(object);
420 45 : }
421 :
422 79 : void AppendClosingLine(Handle<Object> closing_key) {
423 79 : builder_.AppendCString(kEndPrefix);
424 79 : AppendKey(closing_key);
425 : builder_.AppendCString(" closes the circle");
426 79 : }
427 :
428 10 : void AppendEllipsis() {
429 10 : builder_.AppendCString(kLinePrefix);
430 : builder_.AppendCString("...");
431 10 : }
432 :
433 79 : MaybeHandle<String> Finish() { return builder_.Finish(); }
434 :
435 : private:
436 124 : void AppendConstructorName(Handle<Object> object) {
437 124 : builder_.AppendCharacter('\'');
438 : Handle<String> constructor_name =
439 124 : JSReceiver::GetConstructorName(Handle<JSReceiver>::cast(object));
440 124 : builder_.AppendString(constructor_name);
441 : builder_.AppendCharacter('\'');
442 124 : }
443 :
444 : // A key can either be a string, the empty string or a Smi.
445 124 : void AppendKey(Handle<Object> key) {
446 124 : if (key->IsSmi()) {
447 51 : builder_.AppendCString("index ");
448 51 : AppendSmi(Smi::cast(*key));
449 : return;
450 : }
451 :
452 73 : CHECK(key->IsString());
453 : Handle<String> key_as_string = Handle<String>::cast(key);
454 73 : if (key_as_string->length() == 0) {
455 0 : builder_.AppendCString("<anonymous>");
456 : } else {
457 73 : builder_.AppendCString("property '");
458 73 : builder_.AppendString(key_as_string);
459 : builder_.AppendCharacter('\'');
460 : }
461 : }
462 :
463 51 : void AppendSmi(Smi smi) {
464 : static const int kBufferSize = 100;
465 : char chars[kBufferSize];
466 : Vector<char> buffer(chars, kBufferSize);
467 51 : builder_.AppendCString(IntToCString(smi->value(), buffer));
468 51 : }
469 :
470 : IncrementalStringBuilder builder_;
471 : static constexpr const char* kStartPrefix = "\n --> ";
472 : static constexpr const char* kEndPrefix = "\n --- ";
473 : static constexpr const char* kLinePrefix = "\n | ";
474 : };
475 :
476 79 : Handle<String> JsonStringifier::ConstructCircularStructureErrorMessage(
477 : Handle<Object> last_key, size_t start_index) {
478 : DCHECK(start_index < stack_.size());
479 79 : CircularStructureMessageBuilder builder(isolate_);
480 :
481 : // We track the index to be printed next for better readability.
482 79 : size_t index = start_index;
483 79 : const size_t stack_size = stack_.size();
484 :
485 158 : builder.AppendStartLine(stack_[index++].second);
486 :
487 : // Append a maximum of kCircularErrorMessagePrefixCount normal lines.
488 : const size_t prefix_end =
489 158 : std::min(stack_size, index + kCircularErrorMessagePrefixCount);
490 139 : for (; index < prefix_end; ++index) {
491 30 : builder.AppendNormalLine(stack_[index].first, stack_[index].second);
492 : }
493 :
494 : // If the circle consists of too many objects, we skip them and just
495 : // print an ellipsis.
496 79 : if (stack_size > index + kCircularErrorMessagePostfixCount) {
497 10 : builder.AppendEllipsis();
498 : }
499 :
500 : // Since we calculate the postfix lines from the back of the stack,
501 : // we have to ensure that lines are not printed twice.
502 158 : index = std::max(index, stack_size - kCircularErrorMessagePostfixCount);
503 109 : for (; index < stack_size; ++index) {
504 15 : builder.AppendNormalLine(stack_[index].first, stack_[index].second);
505 : }
506 :
507 79 : builder.AppendClosingLine(last_key);
508 :
509 : Handle<String> result;
510 79 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, result, builder.Finish(),
511 : factory()->empty_string());
512 79 : return result;
513 : }
514 :
515 : template <bool deferred_string_key>
516 12059266 : JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object,
517 : bool comma,
518 : Handle<Object> key) {
519 12059266 : StackLimitCheck interrupt_check(isolate_);
520 12059266 : Handle<Object> initial_value = object;
521 12059458 : if (interrupt_check.InterruptRequested() &&
522 384 : isolate_->stack_guard()->HandleInterrupts()->IsException(isolate_)) {
523 : return EXCEPTION;
524 : }
525 23701957 : if (object->IsJSReceiver() || object->IsBigInt()) {
526 833276 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
527 : isolate_, object, ApplyToJsonFunction(object, key), EXCEPTION);
528 : }
529 12059204 : if (!replacer_function_.is_null()) {
530 9939170 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
531 : isolate_, object, ApplyReplacerFunction(object, key, initial_value),
532 : EXCEPTION);
533 : }
534 :
535 12059204 : if (object->IsSmi()) {
536 : if (deferred_string_key) SerializeDeferredKey(comma, key);
537 230809 : return SerializeSmi(Smi::cast(*object));
538 : }
539 :
540 11828395 : switch (HeapObject::cast(*object)->map()->instance_type()) {
541 : case HEAP_NUMBER_TYPE:
542 : case MUTABLE_HEAP_NUMBER_TYPE:
543 : if (deferred_string_key) SerializeDeferredKey(comma, key);
544 1543 : return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
545 : case BIGINT_TYPE:
546 72 : isolate_->Throw(
547 36 : *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON));
548 36 : return EXCEPTION;
549 : case ODDBALL_TYPE:
550 9696386 : switch (Oddball::cast(*object)->kind()) {
551 : case Oddball::kFalse:
552 : if (deferred_string_key) SerializeDeferredKey(comma, key);
553 81 : builder_.AppendCString("false");
554 : return SUCCESS;
555 : case Oddball::kTrue:
556 : if (deferred_string_key) SerializeDeferredKey(comma, key);
557 153 : builder_.AppendCString("true");
558 : return SUCCESS;
559 : case Oddball::kNull:
560 : if (deferred_string_key) SerializeDeferredKey(comma, key);
561 190 : builder_.AppendCString("null");
562 : return SUCCESS;
563 : default:
564 : return UNCHANGED;
565 : }
566 : case JS_ARRAY_TYPE:
567 : if (deferred_string_key) SerializeDeferredKey(comma, key);
568 15859 : return SerializeJSArray(Handle<JSArray>::cast(object), key);
569 : case JS_VALUE_TYPE:
570 : if (deferred_string_key) SerializeDeferredKey(comma, key);
571 702 : return SerializeJSValue(Handle<JSValue>::cast(object), key);
572 : case SYMBOL_TYPE:
573 : return UNCHANGED;
574 : default:
575 2113833 : if (object->IsString()) {
576 : if (deferred_string_key) SerializeDeferredKey(comma, key);
577 1714376 : SerializeString(Handle<String>::cast(object));
578 1714376 : return SUCCESS;
579 : } else {
580 : DCHECK(object->IsJSReceiver());
581 399457 : if (object->IsCallable()) return UNCHANGED;
582 : // Go to slow path for global proxy and objects requiring access checks.
583 : if (deferred_string_key) SerializeDeferredKey(comma, key);
584 397081 : if (object->IsJSProxy()) {
585 410 : return SerializeJSProxy(Handle<JSProxy>::cast(object), key);
586 : }
587 396671 : return SerializeJSObject(Handle<JSObject>::cast(object), key);
588 : }
589 : }
590 :
591 : UNREACHABLE();
592 : }
593 :
594 702 : JsonStringifier::Result JsonStringifier::SerializeJSValue(
595 : Handle<JSValue> object, Handle<Object> key) {
596 : Object raw = object->value();
597 702 : if (raw->IsString()) {
598 : Handle<Object> value;
599 522 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
600 : isolate_, value, Object::ToString(isolate_, object), EXCEPTION);
601 261 : SerializeString(Handle<String>::cast(value));
602 441 : } else if (raw->IsNumber()) {
603 : Handle<Object> value;
604 450 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
605 : isolate_, value, Object::ToNumber(isolate_, object), EXCEPTION);
606 225 : if (value->IsSmi()) return SerializeSmi(Smi::cast(*value));
607 : SerializeHeapNumber(Handle<HeapNumber>::cast(value));
608 216 : } else if (raw->IsBigInt()) {
609 54 : isolate_->Throw(
610 108 : *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON));
611 54 : return EXCEPTION;
612 162 : } else if (raw->IsBoolean()) {
613 108 : builder_.AppendCString(raw->IsTrue(isolate_) ? "true" : "false");
614 : } else {
615 : // ES6 24.3.2.1 step 10.c, serialize as an ordinary JSObject.
616 : return SerializeJSObject(object, key);
617 : }
618 : return SUCCESS;
619 : }
620 :
621 236463 : JsonStringifier::Result JsonStringifier::SerializeSmi(Smi object) {
622 : static const int kBufferSize = 100;
623 : char chars[kBufferSize];
624 : Vector<char> buffer(chars, kBufferSize);
625 236463 : builder_.AppendCString(IntToCString(object->value(), buffer));
626 236463 : return SUCCESS;
627 : }
628 :
629 2111 : JsonStringifier::Result JsonStringifier::SerializeDouble(double number) {
630 2111 : if (std::isinf(number) || std::isnan(number)) {
631 162 : builder_.AppendCString("null");
632 : return SUCCESS;
633 : }
634 : static const int kBufferSize = 100;
635 : char chars[kBufferSize];
636 : Vector<char> buffer(chars, kBufferSize);
637 1949 : builder_.AppendCString(DoubleToCString(number, buffer));
638 : return SUCCESS;
639 : }
640 :
641 : JsonStringifier::Result JsonStringifier::SerializeJSArray(
642 : Handle<JSArray> object, Handle<Object> key) {
643 22272 : HandleScope handle_scope(isolate_);
644 15859 : Result stack_push = StackPush(object, key);
645 15859 : if (stack_push != SUCCESS) return stack_push;
646 15814 : uint32_t length = 0;
647 25215 : CHECK(object->length()->ToArrayLength(&length));
648 : DCHECK(!object->IsAccessCheckNeeded());
649 9401 : builder_.AppendCharacter('[');
650 : Indent();
651 : uint32_t i = 0;
652 15814 : if (replacer_function_.is_null()) {
653 11750 : switch (object->GetElementsKind()) {
654 : case PACKED_SMI_ELEMENTS: {
655 1490 : Handle<FixedArray> elements(FixedArray::cast(object->elements()),
656 1006 : isolate_);
657 1006 : StackLimitCheck interrupt_check(isolate_);
658 6471 : while (i < length) {
659 5465 : if (interrupt_check.InterruptRequested() &&
660 0 : isolate_->stack_guard()->HandleInterrupts()->IsException(
661 : isolate_)) {
662 0 : return EXCEPTION;
663 : }
664 : Separator(i == 0);
665 10930 : SerializeSmi(Smi::cast(elements->get(i)));
666 5465 : i++;
667 : }
668 1006 : break;
669 : }
670 : case PACKED_DOUBLE_ELEMENTS: {
671 : // Empty array is FixedArray but not FixedDoubleArray.
672 517 : if (length == 0) break;
673 : Handle<FixedDoubleArray> elements(
674 1500 : FixedDoubleArray::cast(object->elements()), isolate_);
675 508 : StackLimitCheck interrupt_check(isolate_);
676 1040 : while (i < length) {
677 532 : if (interrupt_check.InterruptRequested() &&
678 0 : isolate_->stack_guard()->HandleInterrupts()->IsException(
679 : isolate_)) {
680 0 : return EXCEPTION;
681 : }
682 : Separator(i == 0);
683 1064 : SerializeDouble(elements->get_scalar(i));
684 532 : i++;
685 : }
686 : break;
687 : }
688 : case PACKED_ELEMENTS: {
689 13418 : Handle<Object> old_length(object->length(), isolate_);
690 378791 : while (i < length) {
691 755041 : if (object->length() != *old_length ||
692 4160 : object->GetElementsKind() != PACKED_ELEMENTS) {
693 : // Fall back to slow path.
694 : break;
695 : }
696 : Separator(i == 0);
697 752880 : Result result = SerializeElement(
698 : isolate_,
699 4160 : Handle<Object>(FixedArray::cast(object->elements())->get(i),
700 : isolate_),
701 : i);
702 375400 : if (result == UNCHANGED) {
703 : builder_.AppendCString("null");
704 375236 : } else if (result != SUCCESS) {
705 : return result;
706 : }
707 368997 : i++;
708 : }
709 : break;
710 : }
711 : // The FAST_HOLEY_* cases could be handled in a faster way. They resemble
712 : // the non-holey cases except that a lookup is necessary for holes.
713 : default:
714 : break;
715 : }
716 : }
717 9411 : if (i < length) {
718 : // Slow path for non-fast elements and fall-back in edge case.
719 3921 : Result result = SerializeArrayLikeSlow(object, i, length);
720 3921 : if (result != SUCCESS) return result;
721 : }
722 : Unindent();
723 9355 : if (length > 0) NewLine();
724 : builder_.AppendCharacter(']');
725 6389 : StackPop();
726 : return SUCCESS;
727 : }
728 :
729 3957 : JsonStringifier::Result JsonStringifier::SerializeArrayLikeSlow(
730 : Handle<JSReceiver> object, uint32_t start, uint32_t length) {
731 : // We need to write out at least two characters per array element.
732 : static const int kMaxSerializableArrayLength = String::kMaxLength / 2;
733 3957 : if (length > kMaxSerializableArrayLength) {
734 10 : isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError());
735 5 : return EXCEPTION;
736 : }
737 19303806 : for (uint32_t i = start; i < length; i++) {
738 : Separator(i == 0);
739 : Handle<Object> element;
740 19299956 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
741 : isolate_, element, JSReceiver::GetElement(isolate_, object, i),
742 : EXCEPTION);
743 9649969 : Result result = SerializeElement(isolate_, element, i);
744 9649969 : if (result == SUCCESS) continue;
745 9461514 : if (result == UNCHANGED) {
746 : // Detect overflow sooner for large sparse arrays.
747 9461472 : if (builder_.HasOverflowed()) return EXCEPTION;
748 9461472 : builder_.AppendCString("null");
749 : } else {
750 : return result;
751 : }
752 : }
753 : return SUCCESS;
754 : }
755 :
756 : JsonStringifier::Result JsonStringifier::SerializeJSObject(
757 : Handle<JSObject> object, Handle<Object> key) {
758 793450 : HandleScope handle_scope(isolate_);
759 396779 : Result stack_push = StackPush(object, key);
760 396779 : if (stack_push != SUCCESS) return stack_push;
761 :
762 793184 : if (property_list_.is_null() &&
763 652491 : !object->map()->IsCustomElementsReceiverMap() &&
764 1585702 : object->HasFastProperties() &&
765 652492 : (object->elements() == ReadOnlyRoots(isolate_).empty_fixed_array() ||
766 128126 : object->elements() ==
767 0 : ReadOnlyRoots(isolate_).empty_slow_element_dictionary())) {
768 : DCHECK(!object->IsJSGlobalProxy());
769 : DCHECK(!object->HasIndexedInterceptor());
770 : DCHECK(!object->HasNamedInterceptor());
771 651565 : Handle<Map> map(object->map(), isolate_);
772 267580 : builder_.AppendCharacter('{');
773 : Indent();
774 : bool comma = false;
775 2185333 : for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) {
776 1371870 : Handle<Name> name(map->instance_descriptors()->GetKey(i), isolate_);
777 : // TODO(rossberg): Should this throw?
778 1135208 : if (!name->IsString()) continue;
779 : Handle<String> key = Handle<String>::cast(name);
780 898546 : PropertyDetails details = map->instance_descriptors()->GetDetails(i);
781 898546 : if (details.IsDontEnum()) continue;
782 : Handle<Object> property;
783 2033412 : if (details.location() == kField && *map == object->map()) {
784 : DCHECK_EQ(kData, details.kind());
785 898339 : FieldIndex field_index = FieldIndex::ForDescriptor(*map, i);
786 : property = JSObject::FastPropertyAt(object, details.representation(),
787 898339 : field_index);
788 : } else {
789 3730 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
790 : isolate_, property,
791 : Object::GetPropertyOrElement(isolate_, object, key), EXCEPTION);
792 : }
793 : Result result = SerializeProperty(property, comma, key);
794 898402 : if (!comma && result == SUCCESS) comma = true;
795 898402 : if (result == EXCEPTION) return result;
796 : }
797 : Unindent();
798 391908 : if (comma) NewLine();
799 : builder_.AppendCharacter('}');
800 : } else {
801 1152 : Result result = SerializeJSReceiverSlow(object);
802 1152 : if (result != SUCCESS) return result;
803 : }
804 392907 : StackPop();
805 : return SUCCESS;
806 : }
807 :
808 1508 : JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow(
809 : Handle<JSReceiver> object) {
810 1508 : Handle<FixedArray> contents = property_list_;
811 1508 : if (contents.is_null()) {
812 2476 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
813 : isolate_, contents,
814 : KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
815 : ENUMERABLE_STRINGS,
816 : GetKeysConversion::kConvertToString),
817 : EXCEPTION);
818 : }
819 1490 : builder_.AppendCharacter('{');
820 : Indent();
821 : bool comma = false;
822 20158 : for (int i = 0; i < contents->length(); i++) {
823 9366 : Handle<String> key(String::cast(contents->get(i)), isolate_);
824 : Handle<Object> property;
825 18732 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
826 : isolate_, property, Object::GetPropertyOrElement(isolate_, object, key),
827 : EXCEPTION);
828 : Result result = SerializeProperty(property, comma, key);
829 9366 : if (!comma && result == SUCCESS) comma = true;
830 9366 : if (result == EXCEPTION) return result;
831 : }
832 : Unindent();
833 1458 : if (comma) NewLine();
834 : builder_.AppendCharacter('}');
835 : return SUCCESS;
836 : }
837 :
838 410 : JsonStringifier::Result JsonStringifier::SerializeJSProxy(
839 : Handle<JSProxy> object, Handle<Object> key) {
840 410 : HandleScope scope(isolate_);
841 410 : Result stack_push = StackPush(object, key);
842 410 : if (stack_push != SUCCESS) return stack_push;
843 : Maybe<bool> is_array = Object::IsArray(object);
844 410 : if (is_array.IsNothing()) return EXCEPTION;
845 410 : if (is_array.FromJust()) {
846 : Handle<Object> length_object;
847 126 : ASSIGN_RETURN_ON_EXCEPTION_VALUE(
848 : isolate_, length_object,
849 : Object::GetLengthFromArrayLike(isolate_,
850 : Handle<JSReceiver>::cast(object)),
851 : EXCEPTION);
852 : uint32_t length;
853 45 : if (!length_object->ToUint32(&length)) {
854 : // Technically, we need to be able to handle lengths outside the
855 : // uint32_t range. However, we would run into string size overflow
856 : // if we tried to stringify such an array.
857 18 : isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError());
858 9 : return EXCEPTION;
859 : }
860 36 : builder_.AppendCharacter('[');
861 : Indent();
862 72 : Result result = SerializeArrayLikeSlow(object, 0, length);
863 36 : if (result != SUCCESS) return result;
864 : Unindent();
865 36 : if (length > 0) NewLine();
866 : builder_.AppendCharacter(']');
867 : } else {
868 356 : Result result = SerializeJSReceiverSlow(object);
869 356 : if (result != SUCCESS) return result;
870 : }
871 : StackPop();
872 387 : return SUCCESS;
873 : }
874 :
875 : template <typename SrcChar, typename DestChar>
876 : void JsonStringifier::SerializeStringUnchecked_(
877 : Vector<const SrcChar> src,
878 : IncrementalStringBuilder::NoExtend<DestChar>* dest) {
879 : // Assert that uc16 character is not truncated down to 8 bit.
880 : // The <uc16, char> version of this method must not be called.
881 : DCHECK(sizeof(DestChar) >= sizeof(SrcChar));
882 89275096 : for (int i = 0; i < src.length(); i++) {
883 86719132 : SrcChar c = src[i];
884 87850372 : if (DoNotEscape(c)) {
885 : dest->Append(c);
886 57117 : } else if (FLAG_harmony_json_stringify && c >= 0xD800 && c <= 0xDFFF) {
887 : // The current character is a surrogate.
888 57108 : if (c <= 0xDBFF) {
889 : // The current character is a leading surrogate.
890 58860 : if (i + 1 < src.length()) {
891 : // There is a next character.
892 1752 : SrcChar next = src[i + 1];
893 1752 : if (next >= 0xDC00 && next <= 0xDFFF) {
894 : // The next character is a trailing surrogate, meaning this is a
895 : // surrogate pair.
896 : dest->Append(c);
897 : dest->Append(next);
898 : i++;
899 : } else {
900 : // The next character is not a trailing surrogate. Thus, the
901 : // current character is a lone leading surrogate.
902 : dest->AppendCString("\\u");
903 0 : char* const hex = DoubleToRadixCString(c, 16);
904 : dest->AppendCString(hex);
905 : DeleteArray(hex);
906 : }
907 : } else {
908 : // There is no next character. Thus, the current character is a lone
909 : // leading surrogate.
910 : dest->AppendCString("\\u");
911 27678 : char* const hex = DoubleToRadixCString(c, 16);
912 : dest->AppendCString(hex);
913 : DeleteArray(hex);
914 : }
915 : } else {
916 : // The current character is a lone trailing surrogate. (If it had been
917 : // preceded by a leading surrogate, we would've ended up in the other
918 : // branch earlier on, and the current character would've been handled
919 : // as part of the surrogate pair already.)
920 : dest->AppendCString("\\u");
921 27678 : char* const hex = DoubleToRadixCString(c, 16);
922 : dest->AppendCString(hex);
923 : DeleteArray(hex);
924 : }
925 : } else {
926 5975 : dest->AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
927 : }
928 : }
929 : }
930 :
931 : template <typename SrcChar, typename DestChar>
932 : void JsonStringifier::SerializeString_(Handle<String> string) {
933 : int length = string->length();
934 2575799 : builder_.Append<uint8_t, DestChar>('"');
935 : // We might be able to fit the whole escaped string in the current string
936 : // part, or we might need to allocate.
937 2575799 : if (int worst_case_length = builder_.EscapedLengthIfCurrentPartFits(length)) {
938 : DisallowHeapAllocation no_gc;
939 2849448 : Vector<const SrcChar> vector = string->GetCharVector<SrcChar>(no_gc);
940 : IncrementalStringBuilder::NoExtendBuilder<DestChar> no_extend(
941 1424724 : &builder_, worst_case_length, no_gc);
942 : SerializeStringUnchecked_(vector, &no_extend);
943 : } else {
944 1151075 : FlatStringReader reader(isolate_, string);
945 4883871577 : for (int i = 0; i < reader.length(); i++) {
946 : SrcChar c = reader.Get<SrcChar>(i);
947 4882720502 : if (DoNotEscape(c)) {
948 : builder_.Append<SrcChar, DestChar>(c);
949 529 : } else if (FLAG_harmony_json_stringify && c >= 0xD800 && c <= 0xDFFF) {
950 : // The current character is a surrogate.
951 0 : if (c <= 0xDBFF) {
952 : // The current character is a leading surrogate.
953 0 : if (i + 1 < reader.length()) {
954 : // There is a next character.
955 : SrcChar next = reader.Get<SrcChar>(i + 1);
956 0 : if (next >= 0xDC00 && next <= 0xDFFF) {
957 : // The next character is a trailing surrogate, meaning this is a
958 : // surrogate pair.
959 : builder_.Append<SrcChar, DestChar>(c);
960 : builder_.Append<SrcChar, DestChar>(next);
961 : i++;
962 : } else {
963 : // The next character is not a trailing surrogate. Thus, the
964 : // current character is a lone leading surrogate.
965 : builder_.AppendCString("\\u");
966 0 : char* const hex = DoubleToRadixCString(c, 16);
967 : builder_.AppendCString(hex);
968 : DeleteArray(hex);
969 : }
970 : } else {
971 : // There is no next character. Thus, the current character is a
972 : // lone leading surrogate.
973 : builder_.AppendCString("\\u");
974 0 : char* const hex = DoubleToRadixCString(c, 16);
975 : builder_.AppendCString(hex);
976 : DeleteArray(hex);
977 : }
978 : } else {
979 : // The current character is a lone trailing surrogate. (If it had
980 : // been preceded by a leading surrogate, we would've ended up in the
981 : // other branch earlier on, and the current character would've been
982 : // handled as part of the surrogate pair already.)
983 : builder_.AppendCString("\\u");
984 0 : char* const hex = DoubleToRadixCString(c, 16);
985 : builder_.AppendCString(hex);
986 : DeleteArray(hex);
987 : }
988 : } else {
989 9844731 : builder_.AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]);
990 : }
991 : }
992 : }
993 : builder_.Append<uint8_t, DestChar>('"');
994 : }
995 :
996 : template <>
997 0 : bool JsonStringifier::DoNotEscape(uint8_t c) {
998 : // https://tc39.github.io/ecma262/#table-json-single-character-escapes
999 2484149271 : return c >= 0x23 && c <= 0x7E && c != 0x5C;
1000 : }
1001 :
1002 : template <>
1003 0 : bool JsonStringifier::DoNotEscape(uint16_t c) {
1004 : // https://tc39.github.io/ecma262/#table-json-single-character-escapes
1005 2271794 : return c >= 0x23 && c != 0x5C && c != 0x7F &&
1006 2271256 : (!FLAG_harmony_json_stringify || (c < 0xD800 || c > 0xDFFF));
1007 : }
1008 :
1009 : void JsonStringifier::NewLine() {
1010 11281829 : if (gap_ == nullptr) return;
1011 4810404 : builder_.AppendCharacter('\n');
1012 14706471 : for (int i = 0; i < indent_; i++) builder_.AppendCString(gap_);
1013 : }
1014 :
1015 : void JsonStringifier::Separator(bool first) {
1016 10892537 : if (!first) builder_.AppendCharacter(',');
1017 : NewLine();
1018 : }
1019 :
1020 : void JsonStringifier::SerializeDeferredKey(bool deferred_comma,
1021 : Handle<Object> deferred_key) {
1022 : Separator(!deferred_comma);
1023 861162 : SerializeString(Handle<String>::cast(deferred_key));
1024 861162 : builder_.AppendCharacter(':');
1025 861162 : if (gap_ != nullptr) builder_.AppendCharacter(' ');
1026 : }
1027 :
1028 3294922 : void JsonStringifier::SerializeString(Handle<String> object) {
1029 3294922 : object = String::Flatten(isolate_, object);
1030 3294922 : if (builder_.CurrentEncoding() == String::ONE_BYTE_ENCODING) {
1031 2575746 : if (String::IsOneByteRepresentationUnderneath(*object)) {
1032 : SerializeString_<uint8_t, uint8_t>(object);
1033 : } else {
1034 719123 : builder_.ChangeEncoding();
1035 719123 : SerializeString(object);
1036 : }
1037 : } else {
1038 719176 : if (String::IsOneByteRepresentationUnderneath(*object)) {
1039 : SerializeString_<uint8_t, uc16>(object);
1040 : } else {
1041 : SerializeString_<uc16, uc16>(object);
1042 : }
1043 : }
1044 3294922 : }
1045 :
1046 : } // namespace internal
1047 122036 : } // namespace v8
|