• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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