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 | ~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 | 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 | return Serialize_<false>(object, false, |
53 | Handle<Object>(Smi::FromInt(i), isolate)); |
54 | } |
55 | |
56 | // Serialize a object property. |
57 | // The key may or may not be serialized depending on the property. |
58 | // The key may also serve as argument for the toJSON function. |
59 | V8_INLINE Result SerializeProperty(Handle<Object> object, bool deferred_comma, |
60 | Handle<String> deferred_key) { |
61 | DCHECK(!deferred_key.is_null()); |
62 | return Serialize_<true>(object, deferred_comma, deferred_key); |
63 | } |
64 | |
65 | template <bool deferred_string_key> |
66 | Result Serialize_(Handle<Object> object, bool comma, Handle<Object> key); |
67 | |
68 | V8_INLINE void SerializeDeferredKey(bool deferred_comma, |
69 | Handle<Object> deferred_key); |
70 | |
71 | Result SerializeSmi(Smi object); |
72 | |
73 | Result SerializeDouble(double number); |
74 | V8_INLINE Result SerializeHeapNumber(Handle<HeapNumber> object) { |
75 | 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 | V8_INLINE void Indent() { indent_++; } |
104 | 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 | MaybeHandle<Object> JsonStringify(Isolate* isolate, Handle<Object> object, |
140 | Handle<Object> replacer, Handle<Object> gap) { |
141 | JsonStringifier stringifier(isolate); |
142 | 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 | JsonStringifier::JsonStringifier(Isolate* isolate) |
214 | : isolate_(isolate), |
215 | builder_(isolate), |
216 | gap_(nullptr), |
217 | indent_(0), |
218 | stack_() { |
219 | tojson_string_ = factory()->toJSON_string(); |
220 | } |
221 | |
222 | MaybeHandle<Object> JsonStringifier::Stringify(Handle<Object> object, |
223 | Handle<Object> replacer, |
224 | Handle<Object> gap) { |
225 | if (!InitializeReplacer(replacer)) return MaybeHandle<Object>(); |
226 | if (!gap->IsUndefined(isolate_) && !InitializeGap(gap)) { |
227 | return MaybeHandle<Object>(); |
228 | } |
229 | Result result = SerializeObject(object); |
230 | if (result == UNCHANGED) return factory()->undefined_value(); |
231 | if (result == SUCCESS) return builder_.Finish(); |
232 | DCHECK(result == EXCEPTION); |
233 | return MaybeHandle<Object>(); |
234 | } |
235 | |
236 | 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 | if (is_array.IsNothing()) return false; |
241 | if (is_array.FromJust()) { |
242 | HandleScope handle_scope(isolate_); |
243 | Handle<OrderedHashSet> set = factory()->NewOrderedHashSet(); |
244 | Handle<Object> length_obj; |
245 | 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 | if (!length_obj->ToUint32(&length)) length = kMaxUInt32; |
252 | for (uint32_t i = 0; i < length; i++) { |
253 | Handle<Object> element; |
254 | Handle<String> key; |
255 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
256 | isolate_, element, Object::GetElement(isolate_, replacer, i), false); |
257 | if (element->IsNumber() || element->IsString()) { |
258 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
259 | isolate_, key, Object::ToString(isolate_, element), false); |
260 | } else if (element->IsJSValue()) { |
261 | Handle<Object> value(Handle<JSValue>::cast(element)->value(), isolate_); |
262 | if (value->IsNumber() || value->IsString()) { |
263 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
264 | isolate_, key, Object::ToString(isolate_, element), false); |
265 | } |
266 | } |
267 | if (key.is_null()) continue; |
268 | // Object keys are internalized, so do it here. |
269 | key = factory()->InternalizeString(key); |
270 | set = OrderedHashSet::Add(isolate_, set, key); |
271 | } |
272 | property_list_ = OrderedHashSet::ConvertToKeysArray( |
273 | isolate_, set, GetKeysConversion::kKeepNumbers); |
274 | property_list_ = handle_scope.CloseAndEscape(property_list_); |
275 | } else if (replacer->IsCallable()) { |
276 | replacer_function_ = Handle<JSReceiver>::cast(replacer); |
277 | } |
278 | return true; |
279 | } |
280 | |
281 | bool JsonStringifier::InitializeGap(Handle<Object> gap) { |
282 | DCHECK_NULL(gap_); |
283 | HandleScope scope(isolate_); |
284 | if (gap->IsJSValue()) { |
285 | Handle<Object> value(Handle<JSValue>::cast(gap)->value(), isolate_); |
286 | if (value->IsString()) { |
287 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap, |
288 | Object::ToString(isolate_, gap), false); |
289 | } else if (value->IsNumber()) { |
290 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap, |
291 | Object::ToNumber(isolate_, gap), false); |
292 | } |
293 | } |
294 | |
295 | if (gap->IsString()) { |
296 | Handle<String> gap_string = Handle<String>::cast(gap); |
297 | if (gap_string->length() > 0) { |
298 | int gap_length = std::min(gap_string->length(), 10); |
299 | gap_ = NewArray<uc16>(gap_length + 1); |
300 | String::WriteToFlat(*gap_string, gap_, 0, gap_length); |
301 | for (int i = 0; i < gap_length; i++) { |
302 | if (gap_[i] > String::kMaxOneByteCharCode) { |
303 | builder_.ChangeEncoding(); |
304 | break; |
305 | } |
306 | } |
307 | gap_[gap_length] = '\0'; |
308 | } |
309 | } else if (gap->IsNumber()) { |
310 | int num_value = DoubleToInt32(gap->Number()); |
311 | if (num_value > 0) { |
312 | int gap_length = std::min(num_value, 10); |
313 | gap_ = NewArray<uc16>(gap_length + 1); |
314 | for (int i = 0; i < gap_length; i++) gap_[i] = ' '; |
315 | gap_[gap_length] = '\0'; |
316 | } |
317 | } |
318 | return true; |
319 | } |
320 | |
321 | MaybeHandle<Object> JsonStringifier::ApplyToJsonFunction(Handle<Object> object, |
322 | Handle<Object> key) { |
323 | HandleScope scope(isolate_); |
324 | |
325 | Handle<Object> object_for_lookup = object; |
326 | if (object->IsBigInt()) { |
327 | 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 | LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); |
337 | ASSIGN_RETURN_ON_EXCEPTION(isolate_, fun, Object::GetProperty(&it), Object); |
338 | if (!fun->IsCallable()) return object; |
339 | } |
340 | |
341 | // Call toJSON function. |
342 | if (key->IsSmi()) key = factory()->NumberToString(key); |
343 | Handle<Object> argv[] = {key}; |
344 | ASSIGN_RETURN_ON_EXCEPTION(isolate_, object, |
345 | Execution::Call(isolate_, fun, object, 1, argv), |
346 | Object); |
347 | return scope.CloseAndEscape(object); |
348 | } |
349 | |
350 | MaybeHandle<Object> JsonStringifier::ApplyReplacerFunction( |
351 | Handle<Object> value, Handle<Object> key, Handle<Object> initial_holder) { |
352 | HandleScope scope(isolate_); |
353 | if (key->IsSmi()) key = factory()->NumberToString(key); |
354 | Handle<Object> argv[] = {key, value}; |
355 | Handle<JSReceiver> holder = CurrentHolder(value, initial_holder); |
356 | ASSIGN_RETURN_ON_EXCEPTION( |
357 | isolate_, value, |
358 | Execution::Call(isolate_, replacer_function_, holder, 2, argv), Object); |
359 | return scope.CloseAndEscape(value); |
360 | } |
361 | |
362 | Handle<JSReceiver> JsonStringifier::CurrentHolder( |
363 | Handle<Object> value, Handle<Object> initial_holder) { |
364 | if (stack_.empty()) { |
365 | Handle<JSObject> holder = |
366 | factory()->NewJSObject(isolate_->object_function()); |
367 | JSObject::AddProperty(isolate_, holder, factory()->empty_string(), |
368 | initial_holder, NONE); |
369 | return holder; |
370 | } else { |
371 | return Handle<JSReceiver>(JSReceiver::cast(*stack_.back().second), |
372 | isolate_); |
373 | } |
374 | } |
375 | |
376 | JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object, |
377 | Handle<Object> key) { |
378 | StackLimitCheck check(isolate_); |
379 | if (check.HasOverflowed()) { |
380 | isolate_->StackOverflow(); |
381 | return EXCEPTION; |
382 | } |
383 | |
384 | { |
385 | DisallowHeapAllocation no_allocation; |
386 | for (size_t i = 0; i < stack_.size(); ++i) { |
387 | if (*stack_[i].second == *object) { |
388 | AllowHeapAllocation allow_to_return_error; |
389 | Handle<String> circle_description = |
390 | ConstructCircularStructureErrorMessage(key, i); |
391 | Handle<Object> error = factory()->NewTypeError( |
392 | MessageTemplate::kCircularStructure, circle_description); |
393 | isolate_->Throw(*error); |
394 | return EXCEPTION; |
395 | } |
396 | } |
397 | } |
398 | stack_.emplace_back(key, object); |
399 | return SUCCESS; |
400 | } |
401 | |
402 | void JsonStringifier::StackPop() { stack_.pop_back(); } |
403 | |
404 | class CircularStructureMessageBuilder { |
405 | public: |
406 | explicit CircularStructureMessageBuilder(Isolate* isolate) |
407 | : builder_(isolate) {} |
408 | |
409 | void AppendStartLine(Handle<Object> start_object) { |
410 | builder_.AppendCString(kStartPrefix); |
411 | builder_.AppendCString("starting at object with constructor " ); |
412 | AppendConstructorName(start_object); |
413 | } |
414 | |
415 | void AppendNormalLine(Handle<Object> key, Handle<Object> object) { |
416 | builder_.AppendCString(kLinePrefix); |
417 | AppendKey(key); |
418 | builder_.AppendCString(" -> object with constructor " ); |
419 | AppendConstructorName(object); |
420 | } |
421 | |
422 | void AppendClosingLine(Handle<Object> closing_key) { |
423 | builder_.AppendCString(kEndPrefix); |
424 | AppendKey(closing_key); |
425 | builder_.AppendCString(" closes the circle" ); |
426 | } |
427 | |
428 | void AppendEllipsis() { |
429 | builder_.AppendCString(kLinePrefix); |
430 | builder_.AppendCString("..." ); |
431 | } |
432 | |
433 | MaybeHandle<String> Finish() { return builder_.Finish(); } |
434 | |
435 | private: |
436 | void AppendConstructorName(Handle<Object> object) { |
437 | builder_.AppendCharacter('\''); |
438 | Handle<String> constructor_name = |
439 | JSReceiver::GetConstructorName(Handle<JSReceiver>::cast(object)); |
440 | builder_.AppendString(constructor_name); |
441 | builder_.AppendCharacter('\''); |
442 | } |
443 | |
444 | // A key can either be a string, the empty string or a Smi. |
445 | void AppendKey(Handle<Object> key) { |
446 | if (key->IsSmi()) { |
447 | builder_.AppendCString("index " ); |
448 | AppendSmi(Smi::cast(*key)); |
449 | return; |
450 | } |
451 | |
452 | CHECK(key->IsString()); |
453 | Handle<String> key_as_string = Handle<String>::cast(key); |
454 | if (key_as_string->length() == 0) { |
455 | builder_.AppendCString("<anonymous>" ); |
456 | } else { |
457 | builder_.AppendCString("property '" ); |
458 | builder_.AppendString(key_as_string); |
459 | builder_.AppendCharacter('\''); |
460 | } |
461 | } |
462 | |
463 | void AppendSmi(Smi smi) { |
464 | static const int kBufferSize = 100; |
465 | char chars[kBufferSize]; |
466 | Vector<char> buffer(chars, kBufferSize); |
467 | builder_.AppendCString(IntToCString(smi->value(), buffer)); |
468 | } |
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 | Handle<String> JsonStringifier::ConstructCircularStructureErrorMessage( |
477 | Handle<Object> last_key, size_t start_index) { |
478 | DCHECK(start_index < stack_.size()); |
479 | CircularStructureMessageBuilder builder(isolate_); |
480 | |
481 | // We track the index to be printed next for better readability. |
482 | size_t index = start_index; |
483 | const size_t stack_size = stack_.size(); |
484 | |
485 | builder.AppendStartLine(stack_[index++].second); |
486 | |
487 | // Append a maximum of kCircularErrorMessagePrefixCount normal lines. |
488 | const size_t prefix_end = |
489 | std::min(stack_size, index + kCircularErrorMessagePrefixCount); |
490 | for (; index < prefix_end; ++index) { |
491 | 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 | if (stack_size > index + kCircularErrorMessagePostfixCount) { |
497 | 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 | index = std::max(index, stack_size - kCircularErrorMessagePostfixCount); |
503 | for (; index < stack_size; ++index) { |
504 | builder.AppendNormalLine(stack_[index].first, stack_[index].second); |
505 | } |
506 | |
507 | builder.AppendClosingLine(last_key); |
508 | |
509 | Handle<String> result; |
510 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, result, builder.Finish(), |
511 | factory()->empty_string()); |
512 | return result; |
513 | } |
514 | |
515 | template <bool deferred_string_key> |
516 | JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object, |
517 | bool comma, |
518 | Handle<Object> key) { |
519 | StackLimitCheck interrupt_check(isolate_); |
520 | Handle<Object> initial_value = object; |
521 | if (interrupt_check.InterruptRequested() && |
522 | isolate_->stack_guard()->HandleInterrupts()->IsException(isolate_)) { |
523 | return EXCEPTION; |
524 | } |
525 | if (object->IsJSReceiver() || object->IsBigInt()) { |
526 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
527 | isolate_, object, ApplyToJsonFunction(object, key), EXCEPTION); |
528 | } |
529 | if (!replacer_function_.is_null()) { |
530 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
531 | isolate_, object, ApplyReplacerFunction(object, key, initial_value), |
532 | EXCEPTION); |
533 | } |
534 | |
535 | if (object->IsSmi()) { |
536 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
537 | return SerializeSmi(Smi::cast(*object)); |
538 | } |
539 | |
540 | 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 | return SerializeHeapNumber(Handle<HeapNumber>::cast(object)); |
545 | case BIGINT_TYPE: |
546 | isolate_->Throw( |
547 | *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON)); |
548 | return EXCEPTION; |
549 | case ODDBALL_TYPE: |
550 | switch (Oddball::cast(*object)->kind()) { |
551 | case Oddball::kFalse: |
552 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
553 | builder_.AppendCString("false" ); |
554 | return SUCCESS; |
555 | case Oddball::kTrue: |
556 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
557 | builder_.AppendCString("true" ); |
558 | return SUCCESS; |
559 | case Oddball::kNull: |
560 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
561 | 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 | return SerializeJSArray(Handle<JSArray>::cast(object), key); |
569 | case JS_VALUE_TYPE: |
570 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
571 | return SerializeJSValue(Handle<JSValue>::cast(object), key); |
572 | case SYMBOL_TYPE: |
573 | return UNCHANGED; |
574 | default: |
575 | if (object->IsString()) { |
576 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
577 | SerializeString(Handle<String>::cast(object)); |
578 | return SUCCESS; |
579 | } else { |
580 | DCHECK(object->IsJSReceiver()); |
581 | 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 | if (object->IsJSProxy()) { |
585 | return SerializeJSProxy(Handle<JSProxy>::cast(object), key); |
586 | } |
587 | return SerializeJSObject(Handle<JSObject>::cast(object), key); |
588 | } |
589 | } |
590 | |
591 | UNREACHABLE(); |
592 | } |
593 | |
594 | JsonStringifier::Result JsonStringifier::SerializeJSValue( |
595 | Handle<JSValue> object, Handle<Object> key) { |
596 | Object raw = object->value(); |
597 | if (raw->IsString()) { |
598 | Handle<Object> value; |
599 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
600 | isolate_, value, Object::ToString(isolate_, object), EXCEPTION); |
601 | SerializeString(Handle<String>::cast(value)); |
602 | } else if (raw->IsNumber()) { |
603 | Handle<Object> value; |
604 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
605 | isolate_, value, Object::ToNumber(isolate_, object), EXCEPTION); |
606 | if (value->IsSmi()) return SerializeSmi(Smi::cast(*value)); |
607 | SerializeHeapNumber(Handle<HeapNumber>::cast(value)); |
608 | } else if (raw->IsBigInt()) { |
609 | isolate_->Throw( |
610 | *factory()->NewTypeError(MessageTemplate::kBigIntSerializeJSON)); |
611 | return EXCEPTION; |
612 | } else if (raw->IsBoolean()) { |
613 | 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 | JsonStringifier::Result JsonStringifier::SerializeSmi(Smi object) { |
622 | static const int kBufferSize = 100; |
623 | char chars[kBufferSize]; |
624 | Vector<char> buffer(chars, kBufferSize); |
625 | builder_.AppendCString(IntToCString(object->value(), buffer)); |
626 | return SUCCESS; |
627 | } |
628 | |
629 | JsonStringifier::Result JsonStringifier::SerializeDouble(double number) { |
630 | if (std::isinf(number) || std::isnan(number)) { |
631 | builder_.AppendCString("null" ); |
632 | return SUCCESS; |
633 | } |
634 | static const int kBufferSize = 100; |
635 | char chars[kBufferSize]; |
636 | Vector<char> buffer(chars, kBufferSize); |
637 | builder_.AppendCString(DoubleToCString(number, buffer)); |
638 | return SUCCESS; |
639 | } |
640 | |
641 | JsonStringifier::Result JsonStringifier::SerializeJSArray( |
642 | Handle<JSArray> object, Handle<Object> key) { |
643 | HandleScope handle_scope(isolate_); |
644 | Result stack_push = StackPush(object, key); |
645 | if (stack_push != SUCCESS) return stack_push; |
646 | uint32_t length = 0; |
647 | CHECK(object->length()->ToArrayLength(&length)); |
648 | DCHECK(!object->IsAccessCheckNeeded()); |
649 | builder_.AppendCharacter('['); |
650 | Indent(); |
651 | uint32_t i = 0; |
652 | if (replacer_function_.is_null()) { |
653 | switch (object->GetElementsKind()) { |
654 | case PACKED_SMI_ELEMENTS: { |
655 | Handle<FixedArray> elements(FixedArray::cast(object->elements()), |
656 | isolate_); |
657 | StackLimitCheck interrupt_check(isolate_); |
658 | while (i < length) { |
659 | if (interrupt_check.InterruptRequested() && |
660 | isolate_->stack_guard()->HandleInterrupts()->IsException( |
661 | isolate_)) { |
662 | return EXCEPTION; |
663 | } |
664 | Separator(i == 0); |
665 | SerializeSmi(Smi::cast(elements->get(i))); |
666 | i++; |
667 | } |
668 | break; |
669 | } |
670 | case PACKED_DOUBLE_ELEMENTS: { |
671 | // Empty array is FixedArray but not FixedDoubleArray. |
672 | if (length == 0) break; |
673 | Handle<FixedDoubleArray> elements( |
674 | FixedDoubleArray::cast(object->elements()), isolate_); |
675 | StackLimitCheck interrupt_check(isolate_); |
676 | while (i < length) { |
677 | if (interrupt_check.InterruptRequested() && |
678 | isolate_->stack_guard()->HandleInterrupts()->IsException( |
679 | isolate_)) { |
680 | return EXCEPTION; |
681 | } |
682 | Separator(i == 0); |
683 | SerializeDouble(elements->get_scalar(i)); |
684 | i++; |
685 | } |
686 | break; |
687 | } |
688 | case PACKED_ELEMENTS: { |
689 | Handle<Object> old_length(object->length(), isolate_); |
690 | while (i < length) { |
691 | if (object->length() != *old_length || |
692 | object->GetElementsKind() != PACKED_ELEMENTS) { |
693 | // Fall back to slow path. |
694 | break; |
695 | } |
696 | Separator(i == 0); |
697 | Result result = SerializeElement( |
698 | isolate_, |
699 | Handle<Object>(FixedArray::cast(object->elements())->get(i), |
700 | isolate_), |
701 | i); |
702 | if (result == UNCHANGED) { |
703 | builder_.AppendCString("null" ); |
704 | } else if (result != SUCCESS) { |
705 | return result; |
706 | } |
707 | 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 | if (i < length) { |
718 | // Slow path for non-fast elements and fall-back in edge case. |
719 | Result result = SerializeArrayLikeSlow(object, i, length); |
720 | if (result != SUCCESS) return result; |
721 | } |
722 | Unindent(); |
723 | if (length > 0) NewLine(); |
724 | builder_.AppendCharacter(']'); |
725 | StackPop(); |
726 | return SUCCESS; |
727 | } |
728 | |
729 | 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 | if (length > kMaxSerializableArrayLength) { |
734 | isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError()); |
735 | return EXCEPTION; |
736 | } |
737 | for (uint32_t i = start; i < length; i++) { |
738 | Separator(i == 0); |
739 | Handle<Object> element; |
740 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
741 | isolate_, element, JSReceiver::GetElement(isolate_, object, i), |
742 | EXCEPTION); |
743 | Result result = SerializeElement(isolate_, element, i); |
744 | if (result == SUCCESS) continue; |
745 | if (result == UNCHANGED) { |
746 | // Detect overflow sooner for large sparse arrays. |
747 | if (builder_.HasOverflowed()) return EXCEPTION; |
748 | 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 | HandleScope handle_scope(isolate_); |
759 | Result stack_push = StackPush(object, key); |
760 | if (stack_push != SUCCESS) return stack_push; |
761 | |
762 | if (property_list_.is_null() && |
763 | !object->map()->IsCustomElementsReceiverMap() && |
764 | object->HasFastProperties() && |
765 | (object->elements() == ReadOnlyRoots(isolate_).empty_fixed_array() || |
766 | object->elements() == |
767 | ReadOnlyRoots(isolate_).empty_slow_element_dictionary())) { |
768 | DCHECK(!object->IsJSGlobalProxy()); |
769 | DCHECK(!object->HasIndexedInterceptor()); |
770 | DCHECK(!object->HasNamedInterceptor()); |
771 | Handle<Map> map(object->map(), isolate_); |
772 | builder_.AppendCharacter('{'); |
773 | Indent(); |
774 | bool comma = false; |
775 | for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) { |
776 | Handle<Name> name(map->instance_descriptors()->GetKey(i), isolate_); |
777 | // TODO(rossberg): Should this throw? |
778 | if (!name->IsString()) continue; |
779 | Handle<String> key = Handle<String>::cast(name); |
780 | PropertyDetails details = map->instance_descriptors()->GetDetails(i); |
781 | if (details.IsDontEnum()) continue; |
782 | Handle<Object> property; |
783 | if (details.location() == kField && *map == object->map()) { |
784 | DCHECK_EQ(kData, details.kind()); |
785 | FieldIndex field_index = FieldIndex::ForDescriptor(*map, i); |
786 | property = JSObject::FastPropertyAt(object, details.representation(), |
787 | field_index); |
788 | } else { |
789 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
790 | isolate_, property, |
791 | Object::GetPropertyOrElement(isolate_, object, key), EXCEPTION); |
792 | } |
793 | Result result = SerializeProperty(property, comma, key); |
794 | if (!comma && result == SUCCESS) comma = true; |
795 | if (result == EXCEPTION) return result; |
796 | } |
797 | Unindent(); |
798 | if (comma) NewLine(); |
799 | builder_.AppendCharacter('}'); |
800 | } else { |
801 | Result result = SerializeJSReceiverSlow(object); |
802 | if (result != SUCCESS) return result; |
803 | } |
804 | StackPop(); |
805 | return SUCCESS; |
806 | } |
807 | |
808 | JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow( |
809 | Handle<JSReceiver> object) { |
810 | Handle<FixedArray> contents = property_list_; |
811 | if (contents.is_null()) { |
812 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
813 | isolate_, contents, |
814 | KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, |
815 | ENUMERABLE_STRINGS, |
816 | GetKeysConversion::kConvertToString), |
817 | EXCEPTION); |
818 | } |
819 | builder_.AppendCharacter('{'); |
820 | Indent(); |
821 | bool comma = false; |
822 | for (int i = 0; i < contents->length(); i++) { |
823 | Handle<String> key(String::cast(contents->get(i)), isolate_); |
824 | Handle<Object> property; |
825 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
826 | isolate_, property, Object::GetPropertyOrElement(isolate_, object, key), |
827 | EXCEPTION); |
828 | Result result = SerializeProperty(property, comma, key); |
829 | if (!comma && result == SUCCESS) comma = true; |
830 | if (result == EXCEPTION) return result; |
831 | } |
832 | Unindent(); |
833 | if (comma) NewLine(); |
834 | builder_.AppendCharacter('}'); |
835 | return SUCCESS; |
836 | } |
837 | |
838 | JsonStringifier::Result JsonStringifier::SerializeJSProxy( |
839 | Handle<JSProxy> object, Handle<Object> key) { |
840 | HandleScope scope(isolate_); |
841 | Result stack_push = StackPush(object, key); |
842 | if (stack_push != SUCCESS) return stack_push; |
843 | Maybe<bool> is_array = Object::IsArray(object); |
844 | if (is_array.IsNothing()) return EXCEPTION; |
845 | if (is_array.FromJust()) { |
846 | Handle<Object> length_object; |
847 | 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 | 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 | isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError()); |
858 | return EXCEPTION; |
859 | } |
860 | builder_.AppendCharacter('['); |
861 | Indent(); |
862 | Result result = SerializeArrayLikeSlow(object, 0, length); |
863 | if (result != SUCCESS) return result; |
864 | Unindent(); |
865 | if (length > 0) NewLine(); |
866 | builder_.AppendCharacter(']'); |
867 | } else { |
868 | Result result = SerializeJSReceiverSlow(object); |
869 | if (result != SUCCESS) return result; |
870 | } |
871 | StackPop(); |
872 | 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 | for (int i = 0; i < src.length(); i++) { |
883 | SrcChar c = src[i]; |
884 | if (DoNotEscape(c)) { |
885 | dest->Append(c); |
886 | } else if (FLAG_harmony_json_stringify && c >= 0xD800 && c <= 0xDFFF) { |
887 | // The current character is a surrogate. |
888 | if (c <= 0xDBFF) { |
889 | // The current character is a leading surrogate. |
890 | if (i + 1 < src.length()) { |
891 | // There is a next character. |
892 | SrcChar next = src[i + 1]; |
893 | 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 | 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 | 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 | char* const hex = DoubleToRadixCString(c, 16); |
922 | dest->AppendCString(hex); |
923 | DeleteArray(hex); |
924 | } |
925 | } else { |
926 | 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 | 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 | if (int worst_case_length = builder_.EscapedLengthIfCurrentPartFits(length)) { |
938 | DisallowHeapAllocation no_gc; |
939 | Vector<const SrcChar> vector = string->GetCharVector<SrcChar>(no_gc); |
940 | IncrementalStringBuilder::NoExtendBuilder<DestChar> no_extend( |
941 | &builder_, worst_case_length, no_gc); |
942 | SerializeStringUnchecked_(vector, &no_extend); |
943 | } else { |
944 | FlatStringReader reader(isolate_, string); |
945 | for (int i = 0; i < reader.length(); i++) { |
946 | SrcChar c = reader.Get<SrcChar>(i); |
947 | if (DoNotEscape(c)) { |
948 | builder_.Append<SrcChar, DestChar>(c); |
949 | } else if (FLAG_harmony_json_stringify && c >= 0xD800 && c <= 0xDFFF) { |
950 | // The current character is a surrogate. |
951 | if (c <= 0xDBFF) { |
952 | // The current character is a leading surrogate. |
953 | if (i + 1 < reader.length()) { |
954 | // There is a next character. |
955 | SrcChar next = reader.Get<SrcChar>(i + 1); |
956 | 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 | 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 | 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 | char* const hex = DoubleToRadixCString(c, 16); |
985 | builder_.AppendCString(hex); |
986 | DeleteArray(hex); |
987 | } |
988 | } else { |
989 | builder_.AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]); |
990 | } |
991 | } |
992 | } |
993 | builder_.Append<uint8_t, DestChar>('"'); |
994 | } |
995 | |
996 | template <> |
997 | bool JsonStringifier::DoNotEscape(uint8_t c) { |
998 | // https://tc39.github.io/ecma262/#table-json-single-character-escapes |
999 | return c >= 0x23 && c <= 0x7E && c != 0x5C; |
1000 | } |
1001 | |
1002 | template <> |
1003 | bool JsonStringifier::DoNotEscape(uint16_t c) { |
1004 | // https://tc39.github.io/ecma262/#table-json-single-character-escapes |
1005 | return c >= 0x23 && c != 0x5C && c != 0x7F && |
1006 | (!FLAG_harmony_json_stringify || (c < 0xD800 || c > 0xDFFF)); |
1007 | } |
1008 | |
1009 | void JsonStringifier::NewLine() { |
1010 | if (gap_ == nullptr) return; |
1011 | builder_.AppendCharacter('\n'); |
1012 | for (int i = 0; i < indent_; i++) builder_.AppendCString(gap_); |
1013 | } |
1014 | |
1015 | void JsonStringifier::Separator(bool first) { |
1016 | 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 | SerializeString(Handle<String>::cast(deferred_key)); |
1024 | builder_.AppendCharacter(':'); |
1025 | if (gap_ != nullptr) builder_.AppendCharacter(' '); |
1026 | } |
1027 | |
1028 | void JsonStringifier::SerializeString(Handle<String> object) { |
1029 | object = String::Flatten(isolate_, object); |
1030 | if (builder_.CurrentEncoding() == String::ONE_BYTE_ENCODING) { |
1031 | if (String::IsOneByteRepresentationUnderneath(*object)) { |
1032 | SerializeString_<uint8_t, uint8_t>(object); |
1033 | } else { |
1034 | builder_.ChangeEncoding(); |
1035 | SerializeString(object); |
1036 | } |
1037 | } else { |
1038 | if (String::IsOneByteRepresentationUnderneath(*object)) { |
1039 | SerializeString_<uint8_t, uc16>(object); |
1040 | } else { |
1041 | SerializeString_<uc16, uc16>(object); |
1042 | } |
1043 | } |
1044 | } |
1045 | |
1046 | } // namespace internal |
1047 | } // namespace v8 |
1048 | |