1 /* Copyright JS Foundation and other contributors, http://js.foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecma-alloc.h"
17 #include "ecma-array-object.h"
18 #include "ecma-builtins.h"
19 #include "ecma-conversion.h"
20 #include "ecma-exceptions.h"
21 #include "ecma-function-object.h"
22 #include "ecma-gc.h"
23 #include "ecma-globals.h"
24 #include "ecma-helpers.h"
25 #include "ecma-builtin-helpers.h"
26 #include "ecma-objects.h"
27 #include "ecma-objects-general.h"
28 #include "jrt.h"
29 #include "jrt-libc-includes.h"
30 #include "lit-char-helpers.h"
31 #include "lit-globals.h"
32
33 #if ENABLED (JERRY_BUILTIN_JSON)
34
35 #define ECMA_BUILTINS_INTERNAL
36 #include "ecma-builtins-internal.h"
37
38 #define BUILTIN_INC_HEADER_NAME "ecma-builtin-json.inc.h"
39 #define BUILTIN_UNDERSCORED_ID json
40 #include "ecma-builtin-internal-routines-template.inc.h"
41
42 /**
43 * The number of expected hexidecimal characters in a hex escape sequence
44 */
45 #define ECMA_JSON_HEX_ESCAPE_SEQUENCE_LENGTH (4)
46
47 /** \addtogroup ecma ECMA
48 * @{
49 *
50 * \addtogroup ecmabuiltins
51 * @{
52 *
53 * \addtogroup json ECMA JSON object built-in
54 * @{
55 */
56
57 /**
58 * JSON token type
59 */
60 typedef enum
61 {
62 TOKEN_INVALID, /**< error token */
63 TOKEN_END, /**< end of stream reached */
64 TOKEN_NUMBER, /**< JSON number */
65 TOKEN_STRING, /**< JSON string */
66 TOKEN_NULL, /**< JSON null primitive value */
67 TOKEN_TRUE, /**< JSON true primitive value */
68 TOKEN_FALSE, /**< JSON false primitive value */
69 TOKEN_LEFT_BRACE, /**< JSON left brace */
70 TOKEN_RIGHT_BRACE, /**< JSON right brace */
71 TOKEN_LEFT_SQUARE, /**< JSON left square bracket */
72 TOKEN_RIGHT_SQUARE, /**< JSON right square bracket */
73 TOKEN_COMMA, /**< JSON comma */
74 TOKEN_COLON /**< JSON colon */
75 } ecma_json_token_type_t;
76
77 /**
78 * JSON token
79 */
80 typedef struct
81 {
82 ecma_json_token_type_t type; /**< type of the current token */
83 const lit_utf8_byte_t *current_p; /**< current position of the string processed by the parser */
84 const lit_utf8_byte_t *end_p; /**< end of the string processed by the parser */
85
86 /**
87 * Fields depending on type.
88 */
89 union
90 {
91 ecma_string_t *string_p; /**< when type is string_token it contains the string */
92 ecma_number_t number; /**< when type is number_token, it contains the value of the number */
93 } u;
94 } ecma_json_token_t;
95
96 /**
97 * Parse and extract string token.
98 */
99 static void
ecma_builtin_json_parse_string(ecma_json_token_t * token_p)100 ecma_builtin_json_parse_string (ecma_json_token_t *token_p) /**< token argument */
101 {
102 const lit_utf8_byte_t *current_p = token_p->current_p;
103 const lit_utf8_byte_t *end_p = token_p->end_p;
104
105 ecma_stringbuilder_t result_builder = ecma_stringbuilder_create ();
106 const lit_utf8_byte_t *unappended_p = current_p;
107
108 while (true)
109 {
110 if (current_p >= end_p || *current_p <= 0x1f)
111 {
112 goto invalid_string;
113 }
114
115 if (*current_p == LIT_CHAR_DOUBLE_QUOTE)
116 {
117 break;
118 }
119
120 if (*current_p == LIT_CHAR_BACKSLASH)
121 {
122 ecma_stringbuilder_append_raw (&result_builder,
123 unappended_p,
124 (lit_utf8_size_t) (current_p - unappended_p));
125
126 current_p++;
127
128 /* If there is an escape sequence but there's no escapable character just return */
129 if (current_p >= end_p)
130 {
131 goto invalid_string;
132 }
133
134 const lit_utf8_byte_t c = *current_p;
135 switch (c)
136 {
137 case LIT_CHAR_DOUBLE_QUOTE:
138 case LIT_CHAR_SLASH:
139 case LIT_CHAR_BACKSLASH:
140 {
141 ecma_stringbuilder_append_byte (&result_builder, c);
142 current_p++;
143 break;
144 }
145 case LIT_CHAR_LOWERCASE_B:
146 {
147 ecma_stringbuilder_append_byte (&result_builder, LIT_CHAR_BS);
148 current_p++;
149 break;
150 }
151 case LIT_CHAR_LOWERCASE_F:
152 {
153 ecma_stringbuilder_append_byte (&result_builder, LIT_CHAR_FF);
154 current_p++;
155 break;
156 }
157 case LIT_CHAR_LOWERCASE_N:
158 {
159 ecma_stringbuilder_append_byte (&result_builder, LIT_CHAR_LF);
160 current_p++;
161 break;
162 }
163 case LIT_CHAR_LOWERCASE_R:
164 {
165 ecma_stringbuilder_append_byte (&result_builder, LIT_CHAR_CR);
166 current_p++;
167 break;
168 }
169 case LIT_CHAR_LOWERCASE_T:
170 {
171 ecma_stringbuilder_append_byte (&result_builder, LIT_CHAR_TAB);
172 current_p++;
173 break;
174 }
175 case LIT_CHAR_LOWERCASE_U:
176 {
177 uint32_t hex_value = lit_char_hex_lookup (current_p + 1, end_p, ECMA_JSON_HEX_ESCAPE_SEQUENCE_LENGTH);
178 if (hex_value == UINT32_MAX)
179 {
180 goto invalid_string;
181 }
182
183 ecma_stringbuilder_append_char (&result_builder, (ecma_char_t) hex_value);
184 current_p += ECMA_JSON_HEX_ESCAPE_SEQUENCE_LENGTH + 1;
185 break;
186 }
187 default:
188 {
189 goto invalid_string;
190 }
191 }
192
193 unappended_p = current_p;
194 continue;
195 }
196
197 current_p++;
198 }
199
200 ecma_stringbuilder_append_raw (&result_builder,
201 unappended_p,
202 (lit_utf8_size_t) (current_p - unappended_p));
203 token_p->u.string_p = ecma_stringbuilder_finalize (&result_builder);
204 token_p->current_p = current_p + 1;
205 token_p->type = TOKEN_STRING;
206 return;
207
208 invalid_string:
209 ecma_stringbuilder_destroy (&result_builder);
210 } /* ecma_builtin_json_parse_string */
211
212 /**
213 * Parse and extract string token.
214 */
215 static void
ecma_builtin_json_parse_number(ecma_json_token_t * token_p)216 ecma_builtin_json_parse_number (ecma_json_token_t *token_p) /**< token argument */
217 {
218 const lit_utf8_byte_t *current_p = token_p->current_p;
219 const lit_utf8_byte_t *end_p = token_p->end_p;
220 const lit_utf8_byte_t *start_p = current_p;
221
222 JERRY_ASSERT (current_p < end_p);
223
224 if (*current_p == LIT_CHAR_MINUS)
225 {
226 current_p++;
227 }
228
229 if (current_p >= end_p)
230 {
231 return;
232 }
233
234 if (*current_p == LIT_CHAR_0)
235 {
236 current_p++;
237
238 if (current_p < end_p && lit_char_is_decimal_digit (*current_p))
239 {
240 return;
241 }
242 }
243 else if (lit_char_is_decimal_digit (*current_p))
244 {
245 do
246 {
247 current_p++;
248 }
249 while (current_p < end_p && lit_char_is_decimal_digit (*current_p));
250 }
251
252 if (current_p < end_p && *current_p == LIT_CHAR_DOT)
253 {
254 current_p++;
255
256 if (current_p >= end_p || !lit_char_is_decimal_digit (*current_p))
257 {
258 return;
259 }
260
261 do
262 {
263 current_p++;
264 }
265 while (current_p < end_p && lit_char_is_decimal_digit (*current_p));
266 }
267
268 if (current_p < end_p && (*current_p == LIT_CHAR_LOWERCASE_E || *current_p == LIT_CHAR_UPPERCASE_E))
269 {
270 current_p++;
271
272 if (current_p < end_p && (*current_p == LIT_CHAR_PLUS || *current_p == LIT_CHAR_MINUS))
273 {
274 current_p++;
275 }
276
277 if (current_p >= end_p || !lit_char_is_decimal_digit (*current_p))
278 {
279 return;
280 }
281
282 do
283 {
284 current_p++;
285 }
286 while (current_p < end_p && lit_char_is_decimal_digit (*current_p));
287 }
288
289 token_p->type = TOKEN_NUMBER;
290 token_p->u.number = ecma_utf8_string_to_number (start_p, (lit_utf8_size_t) (current_p - start_p));
291
292 token_p->current_p = current_p;
293 } /* ecma_builtin_json_parse_number */
294
295 /**
296 * Parse next token.
297 *
298 * The function fills the fields of the ecma_json_token_t
299 * argument and advances the string pointer.
300 */
301 static void
ecma_builtin_json_parse_next_token(ecma_json_token_t * token_p,bool parse_string)302 ecma_builtin_json_parse_next_token (ecma_json_token_t *token_p, /**< token argument */
303 bool parse_string) /**< strings are allowed to parse */
304 {
305 const lit_utf8_byte_t *current_p = token_p->current_p;
306 const lit_utf8_byte_t *end_p = token_p->end_p;
307 token_p->type = TOKEN_INVALID;
308
309 while (current_p < end_p
310 && (*current_p == LIT_CHAR_SP
311 || *current_p == LIT_CHAR_CR
312 || *current_p == LIT_CHAR_LF
313 || *current_p == LIT_CHAR_TAB))
314 {
315 current_p++;
316 }
317
318 if (current_p == end_p)
319 {
320 token_p->type = TOKEN_END;
321 return;
322 }
323
324 switch (*current_p)
325 {
326 case LIT_CHAR_LEFT_BRACE:
327 {
328 token_p->type = TOKEN_LEFT_BRACE;
329 token_p->current_p = current_p + 1;
330 return;
331 }
332 case LIT_CHAR_RIGHT_BRACE:
333 {
334 token_p->type = TOKEN_RIGHT_BRACE;
335 token_p->current_p = current_p + 1;
336 return;
337 }
338 case LIT_CHAR_LEFT_SQUARE:
339 {
340 token_p->type = TOKEN_LEFT_SQUARE;
341 token_p->current_p = current_p + 1;
342 return;
343 }
344 case LIT_CHAR_RIGHT_SQUARE:
345 {
346 token_p->type = TOKEN_RIGHT_SQUARE;
347 token_p->current_p = current_p + 1;
348 return;
349 }
350 case LIT_CHAR_COMMA:
351 {
352 token_p->type = TOKEN_COMMA;
353 token_p->current_p = current_p + 1;
354 return;
355 }
356 case LIT_CHAR_COLON:
357 {
358 token_p->type = TOKEN_COLON;
359 token_p->current_p = current_p + 1;
360 return;
361 }
362 case LIT_CHAR_DOUBLE_QUOTE:
363 {
364 if (parse_string)
365 {
366 token_p->current_p = current_p + 1;
367 ecma_builtin_json_parse_string (token_p);
368 }
369 return;
370 }
371 case LIT_CHAR_LOWERCASE_N:
372 {
373 lit_utf8_size_t size = lit_get_magic_string_size (LIT_MAGIC_STRING_NULL);
374 if (current_p + size <= end_p)
375 {
376 if (!memcmp (lit_get_magic_string_utf8 (LIT_MAGIC_STRING_NULL),
377 current_p,
378 size))
379 {
380 token_p->type = TOKEN_NULL;
381 token_p->current_p = current_p + size;
382 return;
383 }
384 }
385 break;
386 }
387 case LIT_CHAR_LOWERCASE_T:
388 {
389 lit_utf8_size_t size = lit_get_magic_string_size (LIT_MAGIC_STRING_TRUE);
390 if (current_p + size <= end_p)
391 {
392 if (!memcmp (lit_get_magic_string_utf8 (LIT_MAGIC_STRING_TRUE),
393 current_p,
394 size))
395 {
396 token_p->type = TOKEN_TRUE;
397 token_p->current_p = current_p + size;
398 return;
399 }
400 }
401 break;
402 }
403 case LIT_CHAR_LOWERCASE_F:
404 {
405 lit_utf8_size_t size = lit_get_magic_string_size (LIT_MAGIC_STRING_FALSE);
406 if (current_p + size <= end_p)
407 {
408 if (!memcmp (lit_get_magic_string_utf8 (LIT_MAGIC_STRING_FALSE),
409 current_p,
410 size))
411 {
412 token_p->type = TOKEN_FALSE;
413 token_p->current_p = current_p + size;
414 return;
415 }
416 }
417 break;
418 }
419 default:
420 {
421 if (*current_p == LIT_CHAR_MINUS || lit_char_is_decimal_digit (*current_p))
422 {
423 token_p->current_p = current_p;
424 ecma_builtin_json_parse_number (token_p);
425 return;
426 }
427 break;
428 }
429 }
430 } /* ecma_builtin_json_parse_next_token */
431
432 /**
433 * Utility for defining properties.
434 *
435 * It silently ignores all errors.
436 */
437 static void
ecma_builtin_json_define_value_property(ecma_object_t * obj_p,ecma_string_t * property_name_p,ecma_value_t value)438 ecma_builtin_json_define_value_property (ecma_object_t *obj_p, /**< this object */
439 ecma_string_t *property_name_p, /**< property name */
440 ecma_value_t value) /**< value */
441 {
442 ecma_value_t completion_value = ecma_builtin_helper_def_prop (obj_p,
443 property_name_p,
444 value,
445 ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
446
447 JERRY_ASSERT (ecma_is_value_boolean (completion_value));
448 } /* ecma_builtin_json_define_value_property */
449
450 /**
451 * Parse next value.
452 *
453 * The function fills the fields of the ecma_json_token_t
454 * argument and advances the string pointer.
455 *
456 * @return ecma_value with the property value
457 */
458 static ecma_value_t
ecma_builtin_json_parse_value(ecma_json_token_t * token_p)459 ecma_builtin_json_parse_value (ecma_json_token_t *token_p) /**< token argument */
460 {
461 switch (token_p->type)
462 {
463 case TOKEN_NUMBER:
464 {
465 return ecma_make_number_value (token_p->u.number);
466 }
467 case TOKEN_STRING:
468 {
469 return ecma_make_string_value (token_p->u.string_p);
470 }
471 case TOKEN_NULL:
472 {
473 return ECMA_VALUE_NULL;
474 }
475 case TOKEN_TRUE:
476 {
477 return ECMA_VALUE_TRUE;
478 }
479 case TOKEN_FALSE:
480 {
481 return ECMA_VALUE_FALSE;
482 }
483 case TOKEN_LEFT_BRACE:
484 {
485 ecma_object_t *object_p = ecma_op_create_object_object_noarg ();
486
487 ecma_builtin_json_parse_next_token (token_p, true);
488
489 if (token_p->type == TOKEN_RIGHT_BRACE)
490 {
491 return ecma_make_object_value (object_p);
492 }
493
494 while (true)
495 {
496 if (token_p->type != TOKEN_STRING)
497 {
498 break;
499 }
500
501 ecma_string_t *name_p = token_p->u.string_p;
502
503 ecma_builtin_json_parse_next_token (token_p, false);
504 if (token_p->type != TOKEN_COLON)
505 {
506 ecma_deref_ecma_string (name_p);
507 break;
508 }
509
510 ecma_builtin_json_parse_next_token (token_p, true);
511 ecma_value_t value = ecma_builtin_json_parse_value (token_p);
512
513 if (ecma_is_value_empty (value))
514 {
515 ecma_deref_ecma_string (name_p);
516 break;
517 }
518
519 ecma_builtin_json_define_value_property (object_p, name_p, value);
520 ecma_deref_ecma_string (name_p);
521 ecma_free_value (value);
522
523 ecma_builtin_json_parse_next_token (token_p, false);
524 if (token_p->type == TOKEN_RIGHT_BRACE)
525 {
526 return ecma_make_object_value (object_p);
527 }
528
529 if (token_p->type != TOKEN_COMMA)
530 {
531 break;
532 }
533
534 ecma_builtin_json_parse_next_token (token_p, true);
535 }
536
537 /*
538 * Parse error occured.
539 */
540 ecma_deref_object (object_p);
541 return ECMA_VALUE_EMPTY;
542 }
543 case TOKEN_LEFT_SQUARE:
544 {
545 uint32_t length = 0;
546
547 ecma_value_t array_construction = ecma_op_create_array_object (NULL, 0, false);
548 JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (array_construction));
549
550 ecma_object_t *array_p = ecma_get_object_from_value (array_construction);
551
552 ecma_builtin_json_parse_next_token (token_p, true);
553
554 if (token_p->type == TOKEN_RIGHT_SQUARE)
555 {
556 return ecma_make_object_value (array_p);
557 }
558
559 while (true)
560 {
561 ecma_value_t value = ecma_builtin_json_parse_value (token_p);
562
563 if (ecma_is_value_empty (value))
564 {
565 JERRY_ASSERT (token_p->type != TOKEN_STRING);
566 break;
567 }
568
569 ecma_value_t completion;
570 completion = ecma_builtin_helper_def_prop_by_index (array_p,
571 length,
572 value,
573 ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
574 JERRY_ASSERT (ecma_is_value_true (completion));
575 ecma_free_value (value);
576
577 ecma_builtin_json_parse_next_token (token_p, false);
578
579 if (token_p->type == TOKEN_RIGHT_SQUARE)
580 {
581 return ecma_make_object_value (array_p);
582 }
583
584 if (token_p->type != TOKEN_COMMA)
585 {
586 JERRY_ASSERT (token_p->type != TOKEN_STRING);
587 break;
588 }
589
590 ecma_builtin_json_parse_next_token (token_p, true);
591 length++;
592 }
593
594 ecma_deref_object (array_p);
595 return ECMA_VALUE_EMPTY;
596 }
597 default:
598 {
599 return ECMA_VALUE_EMPTY;
600 }
601 }
602 } /* ecma_builtin_json_parse_value */
603
604 /**
605 * Abstract operation InternalizeJSONProperty defined in 24.3.1.1
606 *
607 * @return ecma value
608 * Returned value must be freed with ecma_free_value.
609 */
610 static ecma_value_t
ecma_builtin_json_internalize_property(ecma_object_t * reviver_p,ecma_object_t * holder_p,ecma_string_t * name_p)611 ecma_builtin_json_internalize_property (ecma_object_t *reviver_p, /**< reviver function */
612 ecma_object_t *holder_p, /**< holder object */
613 ecma_string_t *name_p) /**< property name */
614 {
615 JERRY_ASSERT (reviver_p);
616 JERRY_ASSERT (holder_p);
617 JERRY_ASSERT (name_p);
618
619 /* 1. */
620 ecma_value_t value = ecma_op_object_get (holder_p, name_p);
621
622 /* 2. */
623 if (ECMA_IS_VALUE_ERROR (value))
624 {
625 return value;
626 }
627
628 /* 3. */
629 if (ecma_is_value_object (value))
630 {
631 ecma_object_t *object_p = ecma_get_object_from_value (value);
632
633 ecma_collection_t *props_p = ecma_op_object_get_property_names (object_p, ECMA_LIST_ENUMERABLE);
634
635 ecma_value_t *buffer_p = props_p->buffer_p;
636
637 /* 3.d.iii */
638 for (uint32_t i = 0; i < props_p->item_count; i++)
639 {
640 ecma_string_t *property_name_p = ecma_get_string_from_value (buffer_p[i]);
641
642 /* 3.d.iii.1 */
643 ecma_value_t result = ecma_builtin_json_internalize_property (reviver_p, object_p, property_name_p);
644
645 /* 3.d.iii.2 */
646 if (ECMA_IS_VALUE_ERROR (result))
647 {
648 ecma_collection_free (props_p);
649 ecma_deref_object (object_p);
650
651 return result;
652 }
653
654 /* 3.d.iii.3 */
655 if (ecma_is_value_undefined (result))
656 {
657 ecma_value_t delete_val = ecma_op_general_object_delete (object_p,
658 property_name_p,
659 false);
660 JERRY_ASSERT (ecma_is_value_boolean (delete_val));
661 }
662 /* 3.d.iii.4 */
663 else
664 {
665 ecma_builtin_json_define_value_property (object_p,
666 property_name_p,
667 result);
668 ecma_free_value (result);
669 }
670 }
671
672 ecma_collection_free (props_p);
673 }
674
675 ecma_value_t arguments_list[2];
676 arguments_list[0] = ecma_make_string_value (name_p);
677 arguments_list[1] = value;
678
679 /* 4. */
680 ecma_value_t ret_value = ecma_op_function_call (reviver_p,
681 ecma_make_object_value (holder_p),
682 arguments_list,
683 2);
684 ecma_free_value (value);
685 return ret_value;
686 } /* ecma_builtin_json_internalize_property */
687
688 /**
689 * Function to set a string token from the given arguments, fills its fields and advances the string pointer.
690 *
691 * @return ecma_value_t containing an object or an error massage
692 * Returned value must be freed with ecma_free_value.
693 */
694 ecma_value_t
ecma_builtin_json_parse_buffer(const lit_utf8_byte_t * str_start_p,lit_utf8_size_t string_size)695 ecma_builtin_json_parse_buffer (const lit_utf8_byte_t * str_start_p, /**< String to parse */
696 lit_utf8_size_t string_size) /**< size of the string */
697 {
698 ecma_json_token_t token;
699 token.current_p = str_start_p;
700 token.end_p = str_start_p + string_size;
701
702 ecma_builtin_json_parse_next_token (&token, true);
703 ecma_value_t result = ecma_builtin_json_parse_value (&token);
704
705 if (!ecma_is_value_empty (result))
706 {
707 ecma_builtin_json_parse_next_token (&token, false);
708 if (token.type == TOKEN_END)
709 {
710 return result;
711 }
712
713 ecma_free_value (result);
714 }
715
716 return ecma_raise_syntax_error (ECMA_ERR_MSG ("Invalid JSON format."));
717 } /*ecma_builtin_json_parse_buffer*/
718
719 /**
720 * The JSON object's 'parse' routine
721 *
722 * See also:
723 * ECMA-262 v5, 15.12.2
724 *
725 * @return ecma value
726 * Returned value must be freed with ecma_free_value.
727 */
728 static ecma_value_t
ecma_builtin_json_parse(ecma_value_t this_arg,ecma_value_t arg1,ecma_value_t arg2)729 ecma_builtin_json_parse (ecma_value_t this_arg, /**< 'this' argument */
730 ecma_value_t arg1, /**< string argument */
731 ecma_value_t arg2) /**< reviver argument */
732 {
733 JERRY_UNUSED (this_arg);
734
735 ecma_string_t *text_string_p = ecma_op_to_string (arg1);
736
737 if (JERRY_UNLIKELY (text_string_p == NULL))
738 {
739 return ECMA_VALUE_ERROR;
740 }
741
742 ECMA_STRING_TO_UTF8_STRING (text_string_p, str_start_p, string_size);
743 ecma_value_t result = ecma_builtin_json_parse_buffer (str_start_p, string_size);
744 ECMA_FINALIZE_UTF8_STRING (str_start_p, string_size);
745 ecma_deref_ecma_string (text_string_p);
746
747 if (!ECMA_IS_VALUE_ERROR (result) && ecma_op_is_callable (arg2))
748 {
749 ecma_object_t *object_p = ecma_op_create_object_object_noarg ();
750
751 ecma_property_value_t *prop_value_p;
752 prop_value_p = ecma_create_named_data_property (object_p,
753 ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY),
754 ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE,
755 NULL);
756
757 ecma_named_data_property_assign_value (object_p, prop_value_p, result);
758
759 ecma_free_value (result);
760 result = ecma_builtin_json_internalize_property (ecma_get_object_from_value (arg2),
761 object_p,
762 ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY));
763 ecma_deref_object (object_p);
764 }
765
766 return result;
767 } /* ecma_builtin_json_parse */
768
769 /**
770 * Abstract operation 'QuoteJSONString' defined in 24.3.2.2
771 */
772 static void
ecma_builtin_json_quote(ecma_stringbuilder_t * builder_p,ecma_string_t * string_p)773 ecma_builtin_json_quote (ecma_stringbuilder_t *builder_p, /**< builder for the result */
774 ecma_string_t *string_p) /**< string that should be quoted */
775 {
776 ECMA_STRING_TO_UTF8_STRING (string_p, string_buff, string_buff_size);
777 const lit_utf8_byte_t *str_p = string_buff;
778 const lit_utf8_byte_t *regular_str_start_p = string_buff;
779 const lit_utf8_byte_t *str_end_p = str_p + string_buff_size;
780
781 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_DOUBLE_QUOTE);
782
783 while (str_p < str_end_p)
784 {
785 lit_utf8_byte_t c = *str_p++;
786
787 if (c == LIT_CHAR_BACKSLASH || c == LIT_CHAR_DOUBLE_QUOTE)
788 {
789 ecma_stringbuilder_append_raw (builder_p,
790 regular_str_start_p,
791 (lit_utf8_size_t) (str_p - regular_str_start_p - 1));
792 regular_str_start_p = str_p;
793 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_BACKSLASH);
794 ecma_stringbuilder_append_byte (builder_p, c);
795 }
796 else if (c < LIT_CHAR_SP)
797 {
798 ecma_stringbuilder_append_raw (builder_p,
799 regular_str_start_p,
800 (lit_utf8_size_t) (str_p - regular_str_start_p - 1));
801 regular_str_start_p = str_p;
802 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_BACKSLASH);
803 switch (c)
804 {
805 case LIT_CHAR_BS:
806 {
807 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_B);
808 break;
809 }
810 case LIT_CHAR_FF:
811 {
812 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_F);
813 break;
814 }
815 case LIT_CHAR_LF:
816 {
817 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_N);
818 break;
819 }
820 case LIT_CHAR_CR:
821 {
822 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_R);
823 break;
824 }
825 case LIT_CHAR_TAB:
826 {
827 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_T);
828 break;
829 }
830 default: /* Hexadecimal. */
831 {
832 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_LOWERCASE_U);
833 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_0);
834 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_0);
835
836 /* Max range 0-9, hex digits unnecessary. */
837 ecma_stringbuilder_append_byte (builder_p, (lit_utf8_byte_t) (LIT_CHAR_0 + (c >> 4)));
838
839 lit_utf8_byte_t c2 = (c & 0xf);
840 ecma_stringbuilder_append_byte (builder_p,
841 (lit_utf8_byte_t) (c2 + ((c2 <= 9)
842 ? LIT_CHAR_0
843 : (LIT_CHAR_LOWERCASE_A - 10))));
844 break;
845 }
846 }
847 }
848 }
849
850 ecma_stringbuilder_append_raw (builder_p,
851 regular_str_start_p,
852 (lit_utf8_size_t) (str_end_p - regular_str_start_p));
853 ecma_stringbuilder_append_byte (builder_p, LIT_CHAR_DOUBLE_QUOTE);
854
855 ECMA_FINALIZE_UTF8_STRING (string_buff, string_buff_size);
856 } /* ecma_builtin_json_quote */
857
858 static ecma_value_t
859 ecma_builtin_json_serialize_property (ecma_json_stringify_context_t *context_p,
860 ecma_object_t *holder_p,
861 ecma_string_t *key_p);
862
863 /**
864 * Abstract operation 'SerializeJSONObject' defined in 24.3.2.3
865 *
866 * @return ecma value
867 * Returned value must be freed with ecma_free_value.
868 */
869 static ecma_value_t
ecma_builtin_json_serialize_object(ecma_json_stringify_context_t * context_p,ecma_object_t * obj_p)870 ecma_builtin_json_serialize_object (ecma_json_stringify_context_t *context_p, /**< context*/
871 ecma_object_t *obj_p) /**< the object*/
872 {
873 /* 1. */
874 if (ecma_json_has_object_in_stack (context_p->occurence_stack_last_p, obj_p))
875 {
876 return ecma_raise_type_error (ECMA_ERR_MSG ("The structure is cyclical."));
877 }
878
879 /* 2. */
880 ecma_json_occurence_stack_item_t stack_item;
881 stack_item.next_p = context_p->occurence_stack_last_p;
882 stack_item.object_p = obj_p;
883 context_p->occurence_stack_last_p = &stack_item;
884
885 /* 3. - 4.*/
886 const lit_utf8_size_t stepback_size = ecma_stringbuilder_get_size (&context_p->indent_builder);
887 ecma_stringbuilder_append (&context_p->indent_builder, context_p->gap_str_p);
888
889 const bool has_gap = !ecma_compare_ecma_string_to_magic_id (context_p->gap_str_p, LIT_MAGIC_STRING__EMPTY);
890 const lit_utf8_size_t separator_size = ecma_stringbuilder_get_size (&context_p->indent_builder);
891
892 ecma_collection_t *property_keys_p;
893 /* 5. */
894 if (context_p->property_list_p->item_count > 0)
895 {
896 property_keys_p = context_p->property_list_p;
897 }
898 /* 6. */
899 else
900 {
901 property_keys_p = ecma_op_object_get_property_names (obj_p, ECMA_LIST_ENUMERABLE);
902
903 #if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
904 if (property_keys_p == NULL)
905 {
906 return ECMA_VALUE_ERROR;
907 }
908 #endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
909 }
910
911 /* 8. */
912 ecma_value_t *buffer_p = property_keys_p->buffer_p;
913
914 ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_LEFT_BRACE);
915 const lit_utf8_size_t left_brace = ecma_stringbuilder_get_size (&context_p->result_builder);
916 lit_utf8_size_t last_prop = left_brace;
917 ecma_value_t result = ECMA_VALUE_EMPTY;
918
919 for (uint32_t i = 0; i < property_keys_p->item_count; i++)
920 {
921 if (has_gap)
922 {
923 ecma_stringbuilder_append_raw (&context_p->result_builder,
924 ecma_stringbuilder_get_data (&context_p->indent_builder),
925 separator_size);
926 }
927
928 ecma_string_t *key_p = ecma_get_string_from_value (buffer_p[i]);
929 ecma_builtin_json_quote (&context_p->result_builder, key_p);
930 ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_COLON);
931
932 /* 8.c.iii */
933 if (has_gap)
934 {
935 ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_SP);
936 }
937
938 result = ecma_builtin_json_serialize_property (context_p, obj_p, key_p);
939
940 if (ECMA_IS_VALUE_ERROR (result))
941 {
942 goto cleanup;
943 }
944
945 /* 8.b */
946 if (!ecma_is_value_undefined (result))
947 {
948 /* ecma_builtin_json_serialize_property already appended the result. */
949 JERRY_ASSERT (ecma_is_value_empty (result));
950
951 ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_COMMA);
952 last_prop = ecma_stringbuilder_get_size (&context_p->result_builder);
953 }
954 else
955 {
956 /* The property should not be appended, we must backtrack. */
957 ecma_stringbuilder_revert (&context_p->result_builder, last_prop);
958 }
959 }
960
961 if (last_prop != left_brace)
962 {
963 /* Remove the last comma. */
964 ecma_stringbuilder_revert (&context_p->result_builder, last_prop - 1);
965
966 if (has_gap)
967 {
968 /* We appended at least one element, and have a separator, so must append the stepback. */
969 ecma_stringbuilder_append_raw (&context_p->result_builder,
970 ecma_stringbuilder_get_data (&context_p->indent_builder),
971 stepback_size);
972 }
973 }
974
975 ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_RIGHT_BRACE);
976 result = ECMA_VALUE_EMPTY;
977
978 /* 11. */
979 context_p->occurence_stack_last_p = stack_item.next_p;
980
981 /* 12. */
982 ecma_stringbuilder_revert (&context_p->indent_builder, stepback_size);
983
984 cleanup:
985 if (context_p->property_list_p->item_count == 0)
986 {
987 ecma_collection_free (property_keys_p);
988 }
989
990 return result;
991 } /* ecma_builtin_json_serialize_object */
992
993 /**
994 * Abstract operation 'SerializeJSONArray' defined in 24.3.2.4
995 *
996 * @return ecma value
997 * Returned value must be freed with ecma_free_value.
998 */
999 static ecma_value_t
ecma_builtin_json_serialize_array(ecma_json_stringify_context_t * context_p,ecma_object_t * obj_p)1000 ecma_builtin_json_serialize_array (ecma_json_stringify_context_t *context_p, /**< context*/
1001 ecma_object_t *obj_p) /**< the array object*/
1002 {
1003 #ifndef JERRY_NDEBUG
1004 ecma_value_t obj_value = ecma_make_object_value (obj_p);
1005 ecma_value_t is_array = ecma_is_value_array (obj_value);
1006
1007 JERRY_ASSERT (ecma_is_value_true (is_array));
1008 #endif /* !JERRY_NDEBUG */
1009
1010 /* 1. */
1011 if (ecma_json_has_object_in_stack (context_p->occurence_stack_last_p, obj_p))
1012 {
1013 return ecma_raise_type_error (ECMA_ERR_MSG ("The structure is cyclical."));
1014 }
1015
1016 /* 2. */
1017 ecma_json_occurence_stack_item_t stack_item;
1018 stack_item.next_p = context_p->occurence_stack_last_p;
1019 stack_item.object_p = obj_p;
1020 context_p->occurence_stack_last_p = &stack_item;
1021
1022 /* 3. - 4.*/
1023 const lit_utf8_size_t stepback_size = ecma_stringbuilder_get_size (&context_p->indent_builder);
1024 ecma_stringbuilder_append (&context_p->indent_builder, context_p->gap_str_p);
1025 const lit_utf8_size_t separator_size = ecma_stringbuilder_get_size (&context_p->indent_builder);
1026
1027 const bool has_gap = !ecma_compare_ecma_string_to_magic_id (context_p->gap_str_p, LIT_MAGIC_STRING__EMPTY);
1028
1029 /* 6. */
1030 uint32_t array_length;
1031
1032 #if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
1033 if (ECMA_OBJECT_IS_PROXY (obj_p))
1034 {
1035 ecma_value_t length_value = ecma_op_object_get_length (obj_p, &array_length);
1036
1037 if (ECMA_IS_VALUE_ERROR (length_value))
1038 {
1039 return length_value;
1040 }
1041 }
1042 else
1043 #endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
1044 {
1045 array_length = ((ecma_extended_object_t *) obj_p)->u.array.length;
1046 }
1047
1048 ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_LEFT_SQUARE);
1049
1050 const lit_utf8_size_t left_square = ecma_stringbuilder_get_size (&context_p->result_builder);
1051 lit_utf8_size_t last_prop = left_square;
1052
1053 /* 8. - 9. */
1054 for (uint32_t index = 0; index < array_length; index++)
1055 {
1056 /* 9.a */
1057 ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (index);
1058
1059 if (has_gap)
1060 {
1061 ecma_stringbuilder_append_raw (&context_p->result_builder,
1062 ecma_stringbuilder_get_data (&context_p->indent_builder),
1063 separator_size);
1064 }
1065
1066 ecma_value_t result = ecma_builtin_json_serialize_property (context_p, obj_p, index_str_p);
1067 ecma_deref_ecma_string (index_str_p);
1068
1069 if (ECMA_IS_VALUE_ERROR (result))
1070 {
1071 return result;
1072 }
1073
1074 if (ecma_is_value_undefined (result))
1075 {
1076 /* 9.c */
1077 ecma_stringbuilder_append_magic (&context_p->result_builder, LIT_MAGIC_STRING_NULL);
1078 }
1079 else
1080 {
1081 JERRY_ASSERT (ecma_is_value_empty (result));
1082 }
1083
1084 last_prop = ecma_stringbuilder_get_size (&context_p->result_builder);
1085 ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_COMMA);
1086 }
1087
1088 /* Remove the last comma. */
1089 ecma_stringbuilder_revert (&context_p->result_builder, last_prop);
1090
1091 /* 11.b.iii */
1092 if (last_prop != left_square && has_gap)
1093 {
1094 /* We appended at least one element, and have a separator, so must append the stepback. */
1095 ecma_stringbuilder_append_raw (&context_p->result_builder,
1096 ecma_stringbuilder_get_data (&context_p->indent_builder),
1097 stepback_size);
1098 }
1099
1100 ecma_stringbuilder_append_byte (&context_p->result_builder, LIT_CHAR_RIGHT_SQUARE);
1101
1102 /* 12. */
1103 context_p->occurence_stack_last_p = stack_item.next_p;
1104
1105 /* 13. */
1106 ecma_stringbuilder_revert (&context_p->indent_builder, stepback_size);
1107
1108 return ECMA_VALUE_EMPTY;
1109 } /* ecma_builtin_json_serialize_array */
1110
1111 /**
1112 * Abstract operation 'SerializeJSONProperty' defined in 24.3.2.1
1113 *
1114 * @return ecma value
1115 * Returned value must be freed with ecma_free_value.
1116 */
1117 static ecma_value_t
ecma_builtin_json_serialize_property(ecma_json_stringify_context_t * context_p,ecma_object_t * holder_p,ecma_string_t * key_p)1118 ecma_builtin_json_serialize_property (ecma_json_stringify_context_t *context_p, /**< context*/
1119 ecma_object_t *holder_p, /**< the object*/
1120 ecma_string_t *key_p) /**< property key*/
1121 {
1122 /* 1. */
1123 ecma_value_t value = ecma_op_object_get (holder_p, key_p);
1124
1125 /* 2. */
1126 if (ECMA_IS_VALUE_ERROR (value))
1127 {
1128 return value;
1129 }
1130
1131 /* 3. */
1132 if (ecma_is_value_object (value))
1133 {
1134 ecma_object_t *value_obj_p = ecma_get_object_from_value (value);
1135
1136 ecma_value_t to_json = ecma_op_object_get_by_magic_id (value_obj_p, LIT_MAGIC_STRING_TO_JSON_UL);
1137
1138 if (ECMA_IS_VALUE_ERROR (to_json))
1139 {
1140 ecma_deref_object (value_obj_p);
1141 return to_json;
1142 }
1143
1144 /* 3.c */
1145 if (ecma_op_is_callable (to_json))
1146 {
1147 ecma_value_t key_value = ecma_make_string_value (key_p);
1148 ecma_value_t call_args[] = { key_value };
1149 ecma_object_t *to_json_obj_p = ecma_get_object_from_value (to_json);
1150
1151 ecma_value_t result = ecma_op_function_call (to_json_obj_p, value, call_args, 1);
1152 ecma_deref_object (value_obj_p);
1153
1154 if (ECMA_IS_VALUE_ERROR (result))
1155 {
1156 ecma_deref_object (to_json_obj_p);
1157 return result;
1158 }
1159
1160 value = result;
1161 }
1162 ecma_free_value (to_json);
1163 }
1164
1165 /* 4. */
1166 if (context_p->replacer_function_p)
1167 {
1168 ecma_value_t holder_value = ecma_make_object_value (holder_p);
1169 ecma_value_t key_value = ecma_make_string_value (key_p);
1170 ecma_value_t call_args[] = { key_value, value };
1171
1172 ecma_value_t result = ecma_op_function_call (context_p->replacer_function_p, holder_value, call_args, 2);
1173 ecma_free_value (value);
1174
1175 if (ECMA_IS_VALUE_ERROR (result))
1176 {
1177 return result;
1178 }
1179
1180 value = result;
1181 }
1182
1183 /* 5. */
1184 if (ecma_is_value_object (value))
1185 {
1186 ecma_object_t *obj_p = ecma_get_object_from_value (value);
1187 lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_p);
1188
1189 /* 5.a */
1190 if (class_name == LIT_MAGIC_STRING_NUMBER_UL)
1191 {
1192 value = ecma_op_to_number (value);
1193 ecma_deref_object (obj_p);
1194
1195 if (ECMA_IS_VALUE_ERROR (value))
1196 {
1197 return value;
1198 }
1199 }
1200 /* 5.b */
1201 else if (class_name == LIT_MAGIC_STRING_STRING_UL)
1202 {
1203 ecma_string_t *str_p = ecma_op_to_string (value);
1204 ecma_deref_object (obj_p);
1205
1206 if (JERRY_UNLIKELY (str_p == NULL))
1207 {
1208 return ECMA_VALUE_ERROR;
1209 }
1210
1211 value = ecma_make_string_value (str_p);
1212 }
1213 /* 5.c */
1214 else if (class_name == LIT_MAGIC_STRING_BOOLEAN_UL)
1215 {
1216 ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p;
1217 value = ext_object_p->u.class_prop.u.value;
1218 ecma_deref_object (obj_p);
1219 }
1220 }
1221
1222 /* 6. - 8. */
1223 if (ecma_is_value_null (value))
1224 {
1225 ecma_stringbuilder_append_magic (&context_p->result_builder, LIT_MAGIC_STRING_NULL);
1226 return ECMA_VALUE_EMPTY;
1227 }
1228
1229 if (ecma_is_value_true (value))
1230 {
1231 ecma_stringbuilder_append_magic (&context_p->result_builder, LIT_MAGIC_STRING_TRUE);
1232 return ECMA_VALUE_EMPTY;
1233 }
1234
1235 if (ecma_is_value_false (value))
1236 {
1237 ecma_stringbuilder_append_magic (&context_p->result_builder, LIT_MAGIC_STRING_FALSE);
1238 return ECMA_VALUE_EMPTY;
1239 }
1240
1241 /* 9. */
1242 if (ecma_is_value_string (value))
1243 {
1244 ecma_string_t *value_str_p = ecma_get_string_from_value (value);
1245 /* Quote will append the result. */
1246 ecma_builtin_json_quote (&context_p->result_builder, value_str_p);
1247 ecma_deref_ecma_string (value_str_p);
1248
1249 return ECMA_VALUE_EMPTY;
1250 }
1251
1252 /* 10. */
1253 if (ecma_is_value_number (value))
1254 {
1255 ecma_number_t num_value = ecma_get_number_from_value (value);
1256
1257 /* 10.a */
1258 if (!ecma_number_is_nan (num_value) && !ecma_number_is_infinity (num_value))
1259 {
1260 ecma_string_t *result_string_p = ecma_op_to_string (value);
1261 JERRY_ASSERT (result_string_p != NULL);
1262
1263 ecma_stringbuilder_append (&context_p->result_builder, result_string_p);
1264 ecma_deref_ecma_string (result_string_p);
1265 }
1266 else
1267 {
1268 /* 10.b */
1269 ecma_stringbuilder_append_magic (&context_p->result_builder, LIT_MAGIC_STRING_NULL);
1270 }
1271
1272 ecma_free_value (value);
1273 return ECMA_VALUE_EMPTY;
1274 }
1275
1276 /* 11. */
1277 if (ecma_is_value_object (value) && !ecma_op_is_callable (value))
1278 {
1279 ecma_value_t is_array = ecma_is_value_array (value);
1280
1281 #if ENABLED (JERRY_ES2015)
1282 if (ECMA_IS_VALUE_ERROR (is_array))
1283 {
1284 ecma_free_value (value);
1285 return is_array;
1286 }
1287 #endif /* ENABLED (JERRY_ES2015) */
1288
1289 ecma_object_t *obj_p = ecma_get_object_from_value (value);
1290
1291 ecma_value_t ret_value;
1292 /* 10.a */
1293 if (ecma_is_value_true (is_array))
1294 {
1295 ret_value = ecma_builtin_json_serialize_array (context_p, obj_p);
1296 }
1297 /* 10.b */
1298 else
1299 {
1300 ret_value = ecma_builtin_json_serialize_object (context_p, obj_p);
1301 }
1302
1303 ecma_deref_object (obj_p);
1304 return ret_value;
1305 }
1306
1307 /* 12. */
1308 ecma_free_value (value);
1309 return ECMA_VALUE_UNDEFINED;
1310 } /* ecma_builtin_json_serialize_property */
1311
1312 /**
1313 * Helper function to stringify an object in JSON format representing an ecma_value.
1314 *
1315 * @return ecma_value_t string created from an abject formating by a given context
1316 * Returned value must be freed with ecma_free_value.
1317 *
1318 */
ecma_builtin_json_str_helper(ecma_json_stringify_context_t * context_p,const ecma_value_t arg1)1319 static ecma_value_t ecma_builtin_json_str_helper (ecma_json_stringify_context_t *context_p, /**< context argument */
1320 const ecma_value_t arg1) /**< object argument */
1321 {
1322 ecma_value_t ret_value = ECMA_VALUE_EMPTY;
1323 ecma_object_t *obj_wrapper_p = ecma_op_create_object_object_noarg ();
1324 ecma_string_t *empty_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);
1325 ecma_value_t put_comp_val = ecma_builtin_helper_def_prop (obj_wrapper_p,
1326 empty_str_p,
1327 arg1,
1328 ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
1329
1330 JERRY_ASSERT (ecma_is_value_true (put_comp_val));
1331
1332 context_p->result_builder = ecma_stringbuilder_create ();
1333
1334 if (!ecma_compare_ecma_string_to_magic_id (context_p->gap_str_p, LIT_MAGIC_STRING__EMPTY))
1335 {
1336 ecma_stringbuilder_append_byte (&context_p->indent_builder, LIT_CHAR_LF);
1337 }
1338
1339 ret_value = ecma_builtin_json_serialize_property (context_p, obj_wrapper_p, empty_str_p);
1340 ecma_deref_object (obj_wrapper_p);
1341
1342 if (ECMA_IS_VALUE_ERROR (ret_value) || ecma_is_value_undefined (ret_value))
1343 {
1344 ecma_stringbuilder_destroy (&context_p->result_builder);
1345 return ret_value;
1346 }
1347
1348 return ecma_make_string_value (ecma_stringbuilder_finalize (&context_p->result_builder));
1349 } /* ecma_builtin_json_str_helper */
1350
1351 /**
1352 * Function to create a json formated string from an object
1353 *
1354 * @return ecma_value_t containing a json string
1355 * Returned value must be freed with ecma_free_value.
1356 */
1357 ecma_value_t
ecma_builtin_json_string_from_object(const ecma_value_t arg1)1358 ecma_builtin_json_string_from_object (const ecma_value_t arg1) /**< object argument */
1359 {
1360 ecma_json_stringify_context_t context;
1361 context.occurence_stack_last_p = NULL;
1362 context.indent_builder = ecma_stringbuilder_create ();
1363 context.property_list_p = ecma_new_collection ();
1364 context.replacer_function_p = NULL;
1365 context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);
1366
1367 ecma_value_t ret_value = ecma_builtin_json_str_helper (&context, arg1);
1368
1369 ecma_deref_ecma_string (context.gap_str_p);
1370 ecma_stringbuilder_destroy (&context.indent_builder);
1371 ecma_collection_free (context.property_list_p);
1372 return ret_value;
1373 } /*ecma_builtin_json_string_from_object*/
1374
1375 /**
1376 * The JSON object's 'stringify' routine
1377 *
1378 * See also:
1379 * ECMA-262 v5, 15.12.3
1380 *
1381 * @return ecma value
1382 * Returned value must be freed with ecma_free_value.
1383 */
1384 static ecma_value_t
ecma_builtin_json_stringify(ecma_value_t this_arg,ecma_value_t arg1,ecma_value_t arg2,ecma_value_t arg3)1385 ecma_builtin_json_stringify (ecma_value_t this_arg, /**< 'this' argument */
1386 ecma_value_t arg1, /**< value */
1387 ecma_value_t arg2, /**< replacer */
1388 ecma_value_t arg3) /**< space */
1389 {
1390 JERRY_UNUSED (this_arg);
1391
1392 ecma_json_stringify_context_t context;
1393 context.replacer_function_p = NULL;
1394 context.property_list_p = ecma_new_collection ();
1395
1396 /* 4. */
1397 if (ecma_is_value_object (arg2))
1398 {
1399 ecma_object_t *obj_p = ecma_get_object_from_value (arg2);
1400
1401 /* 4.a */
1402 if (ecma_op_is_callable (arg2))
1403 {
1404 context.replacer_function_p = obj_p;
1405 }
1406 /* 4.b */
1407 else if (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_ARRAY)
1408 {
1409 ecma_extended_object_t *array_object_p = (ecma_extended_object_t *) obj_p;
1410 uint32_t array_length = array_object_p->u.array.length;
1411 uint32_t index = 0;
1412
1413 /* 4.b.iii.5 */
1414 while (index < array_length)
1415 {
1416 ecma_value_t value = ecma_op_object_get_by_uint32_index (obj_p, index);
1417
1418 if (ECMA_IS_VALUE_ERROR (value))
1419 {
1420 ecma_collection_free (context.property_list_p);
1421 return value;
1422 }
1423
1424 /* 4.b.iii.5.c */
1425 ecma_value_t item = ECMA_VALUE_UNDEFINED;
1426
1427 /* 4.b.iii.5.d */
1428 if (ecma_is_value_string (value))
1429 {
1430 ecma_ref_ecma_string (ecma_get_string_from_value (value));
1431 item = value;
1432 }
1433 /* 4.b.iii.5.e */
1434 else if (ecma_is_value_number (value))
1435 {
1436 ecma_string_t *number_str_p = ecma_op_to_string (value);
1437 JERRY_ASSERT (number_str_p != NULL);
1438 item = ecma_make_string_value (number_str_p);
1439 }
1440 /* 4.b.iii.5.f */
1441 else if (ecma_is_value_object (value))
1442 {
1443 ecma_object_t *value_obj_p = ecma_get_object_from_value (value);
1444 lit_magic_string_id_t class_id = ecma_object_get_class_name (value_obj_p);
1445
1446 if (class_id == LIT_MAGIC_STRING_NUMBER_UL || class_id == LIT_MAGIC_STRING_STRING_UL)
1447 {
1448 ecma_string_t *str_p = ecma_op_to_string (value);
1449
1450 if (JERRY_UNLIKELY (str_p == NULL))
1451 {
1452 ecma_collection_free (context.property_list_p);
1453 ecma_free_value (value);
1454 return ECMA_VALUE_ERROR;
1455 }
1456
1457 item = ecma_make_string_value (str_p);
1458 }
1459 }
1460
1461 ecma_free_value (value);
1462
1463 /* 4.b.iii.5.g */
1464 if (!ecma_is_value_undefined (item))
1465 {
1466 JERRY_ASSERT (ecma_is_value_string (item));
1467 ecma_string_t *string_p = ecma_get_string_from_value (item);
1468
1469 if (!ecma_has_string_value_in_collection (context.property_list_p, string_p))
1470 {
1471 ecma_collection_push_back (context.property_list_p, item);
1472 }
1473 else
1474 {
1475 ecma_deref_ecma_string (string_p);
1476 }
1477 }
1478
1479 index++;
1480 }
1481 }
1482 }
1483
1484 ecma_value_t space;
1485
1486 /* 5. */
1487 if (ecma_is_value_object (arg3))
1488 {
1489 ecma_object_t *obj_p = ecma_get_object_from_value (arg3);
1490 lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_p);
1491
1492 /* 5.a */
1493 if (class_name == LIT_MAGIC_STRING_NUMBER_UL)
1494 {
1495 ecma_value_t value = ecma_op_to_number (arg3);
1496
1497 if (ECMA_IS_VALUE_ERROR (value))
1498 {
1499 ecma_collection_free (context.property_list_p);
1500 return value;
1501 }
1502
1503 space = value;
1504 }
1505 /* 5.b */
1506 else if (class_name == LIT_MAGIC_STRING_STRING_UL)
1507 {
1508 ecma_string_t *value_str_p = ecma_op_to_string (arg3);
1509
1510 if (JERRY_UNLIKELY (value_str_p == NULL))
1511 {
1512 ecma_collection_free (context.property_list_p);
1513 return ECMA_VALUE_ERROR;
1514 }
1515
1516 space = ecma_make_string_value (value_str_p);
1517 }
1518 else
1519 {
1520 space = ecma_copy_value (arg3);
1521 }
1522 }
1523 else
1524 {
1525 space = ecma_copy_value (arg3);
1526 }
1527
1528 /* 6. */
1529 if (ecma_is_value_number (space))
1530 {
1531 /* 6.a */
1532 ecma_number_t num_of_spaces;
1533 ecma_op_to_integer (space, &num_of_spaces);
1534
1535 num_of_spaces = JERRY_MIN (10, num_of_spaces);
1536
1537 /* 6.b */
1538 if (num_of_spaces < 1)
1539 {
1540 context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);
1541 }
1542 else
1543 {
1544 JMEM_DEFINE_LOCAL_ARRAY (space_buff, num_of_spaces, char);
1545
1546 memset (space_buff, LIT_CHAR_SP, (size_t) num_of_spaces);
1547 context.gap_str_p = ecma_new_ecma_string_from_utf8 ((lit_utf8_byte_t *) space_buff,
1548 (lit_utf8_size_t) num_of_spaces);
1549
1550 JMEM_FINALIZE_LOCAL_ARRAY (space_buff);
1551 }
1552 }
1553 /* 7. */
1554 else if (ecma_is_value_string (space))
1555 {
1556 ecma_string_t *space_str_p = ecma_get_string_from_value (space);
1557 ecma_length_t num_of_chars = ecma_string_get_length (space_str_p);
1558
1559 if (num_of_chars < 10)
1560 {
1561 ecma_ref_ecma_string (space_str_p);
1562 context.gap_str_p = space_str_p;
1563 }
1564 else
1565 {
1566 context.gap_str_p = ecma_string_substr (space_str_p, 0, 10);
1567 }
1568 }
1569 /* 8. */
1570 else
1571 {
1572 context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);
1573 }
1574
1575 ecma_free_value (space);
1576
1577 /* 1., 2., 3. */
1578 context.occurence_stack_last_p = NULL;
1579 context.indent_builder = ecma_stringbuilder_create ();
1580
1581 /* 9. */
1582 ecma_value_t ret_value = ecma_builtin_json_str_helper (&context, arg1);
1583
1584 ecma_deref_ecma_string (context.gap_str_p);
1585 ecma_stringbuilder_destroy (&context.indent_builder);
1586 ecma_collection_free (context.property_list_p);
1587
1588 return ret_value;
1589 } /* ecma_builtin_json_stringify */
1590
1591 /**
1592 * @}
1593 * @}
1594 * @}
1595 */
1596
1597 #endif /* ENABLED (JERRY_BUILTIN_JSON) */
1598