/**
 * Allocate a collection of ecma-strings.
 *
 * @return pointer to the collection's header
 */
ecma_collection_header_t *
ecma_new_strings_collection (ecma_string_t *string_ptrs_buffer[], /**< pointers to ecma-strings */
                             ecma_length_t strings_number) /**< number of ecma-strings */
{
  JERRY_ASSERT (string_ptrs_buffer != NULL);
  JERRY_ASSERT (strings_number > 0);

  ecma_collection_header_t *new_collection_p;

  MEM_DEFINE_LOCAL_ARRAY (values_buffer, strings_number, ecma_value_t);

  for (ecma_length_t string_index = 0;
       string_index < strings_number;
       string_index++)
  {
    values_buffer[string_index] = ecma_make_string_value (string_ptrs_buffer[string_index]);
  }

  new_collection_p = ecma_new_values_collection (values_buffer,
                                                 strings_number,
                                                 false);

  MEM_FINALIZE_LOCAL_ARRAY (values_buffer);

  return new_collection_p;
} /* ecma_new_strings_collection */
/**
 * Fulfill a Promise with a value.
 *
 * See also: ES2015 25.4.1.4
 */
static void
ecma_fulfill_promise (ecma_value_t promise, /**< promise */
                      ecma_value_t value) /**< fulfilled value */
{
  ecma_object_t *obj_p = ecma_get_object_from_value (promise);

  JERRY_ASSERT (ecma_promise_get_state (obj_p) == ECMA_PROMISE_STATE_PENDING);

  ecma_promise_set_state (obj_p, ECMA_PROMISE_STATE_FULFILLED);
  ecma_promise_set_result (obj_p, ecma_copy_value_if_not_object (value));
  ecma_promise_object_t *promise_p = (ecma_promise_object_t *) obj_p;

  /* GC can be triggered by ecma_new_values_collection so freeing the collection
     first and creating a new one might cause a heap after use event. */
  ecma_collection_header_t *reject_reactions = promise_p->reject_reactions;
  ecma_collection_header_t *fulfill_reactions = promise_p->fulfill_reactions;
  promise_p->reject_reactions = ecma_new_values_collection ();
  promise_p->fulfill_reactions = ecma_new_values_collection ();

  /* Reject reactions will never be triggered. */
  ecma_free_values_collection (reject_reactions, ECMA_COLLECTION_NO_REF_OBJECTS);
  ecma_promise_trigger_reactions (fulfill_reactions, value);
} /* ecma_fulfill_promise */
/**
 * Allocate a collection of ecma-strings.
 *
 * @return pointer to the collection's header
 */
ecma_collection_header_t *
ecma_new_strings_collection (ecma_string_t *string_ptrs_buffer[], /**< pointers to ecma-strings */
                             ecma_length_t strings_number) /**< number of ecma-strings */
{
  JERRY_ASSERT (string_ptrs_buffer != NULL || strings_number == 0);

  ecma_collection_header_t *new_collection_p;

  new_collection_p = ecma_new_values_collection (NULL, 0, false);

  for (ecma_length_t string_index = 0;
       string_index < strings_number;
       string_index++)
  {
    ecma_append_to_values_collection (new_collection_p,
                                      ecma_make_string_value (string_ptrs_buffer[string_index]),
                                      false);
  }

  return new_collection_p;
} /* ecma_new_strings_collection */
/**
 * The Function.prototype object's 'bind' routine
 *
 * See also:
 *          ECMA-262 v5, 15.3.4.5
 *
 * @return ecma value
 *         Returned value must be freed with ecma_free_value.
 */
static ecma_value_t
ecma_builtin_function_prototype_object_bind (ecma_value_t this_arg, /**< this argument */
                                             const ecma_value_t *arguments_list_p, /**< list of arguments */
                                             ecma_length_t arguments_number) /**< number of arguments */
{
  ecma_value_t ret_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY);

  /* 2. */
  if (!ecma_op_is_callable (this_arg))
  {
    ret_value = ecma_raise_type_error (ECMA_ERR_MSG (""));
  }
  else
  {
    /* 4. 11. 18. */
    ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE);
    ecma_object_t *function_p = ecma_create_object (prototype_obj_p,
                                                    false,
                                                    true,
                                                    ECMA_OBJECT_TYPE_BOUND_FUNCTION);

    ecma_deref_object (prototype_obj_p);

    /* 7. */
    ecma_value_t *target_function_prop_p;
    target_function_prop_p = ecma_create_internal_property (function_p,
                                                            ECMA_INTERNAL_PROPERTY_BOUND_FUNCTION_TARGET_FUNCTION);

    ecma_object_t *this_arg_obj_p = ecma_get_object_from_value (this_arg);
    ECMA_SET_INTERNAL_VALUE_POINTER (*target_function_prop_p, this_arg_obj_p);

    /* 8. */
    ecma_value_t *bound_this_prop_p;
    bound_this_prop_p = ecma_create_internal_property (function_p, ECMA_INTERNAL_PROPERTY_BOUND_FUNCTION_BOUND_THIS);
    const ecma_length_t arg_count = arguments_number;

    if (arg_count > 0)
    {
      *bound_this_prop_p = ecma_copy_value_if_not_object (arguments_list_p[0]);
    }
    else
    {
      *bound_this_prop_p = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
    }

    if (arg_count > 1)
    {
      ecma_collection_header_t *bound_args_collection_p;
      bound_args_collection_p = ecma_new_values_collection (&arguments_list_p[1], arg_count - 1, false);

      ecma_value_t *bound_args_prop_p;
      bound_args_prop_p = ecma_create_internal_property (function_p, ECMA_INTERNAL_PROPERTY_BOUND_FUNCTION_BOUND_ARGS);
      ECMA_SET_INTERNAL_VALUE_POINTER (*bound_args_prop_p, bound_args_collection_p);
    }

    /*
     * [[Class]] property is not stored explicitly for objects of ECMA_OBJECT_TYPE_FUNCTION type.
     *
     * See also: ecma_object_get_class_name
     */

    /* 16. */
    ecma_number_t length = ECMA_NUMBER_ZERO;
    ecma_string_t *magic_string_length_p = ecma_new_ecma_length_string ();

    /* 15. */
    if (ecma_object_get_class_name (this_arg_obj_p) == LIT_MAGIC_STRING_FUNCTION_UL)
    {
      ecma_value_t get_len_value = ecma_op_object_get (this_arg_obj_p, magic_string_length_p);
      JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (get_len_value));
      JERRY_ASSERT (ecma_is_value_number (get_len_value));

      const ecma_length_t bound_arg_count = arg_count > 1 ? arg_count - 1 : 0;

      /* 15.a */
      length = ecma_get_number_from_value (get_len_value) - ((ecma_number_t) bound_arg_count);
      ecma_free_value (get_len_value);

      /* 15.b */
      if (ecma_number_is_negative (length))
      {
        length = ECMA_NUMBER_ZERO;
      }
    }

    /* 17. */
    ecma_value_t completion = ecma_builtin_helper_def_prop (function_p,
                                                            magic_string_length_p,
                                                            ecma_make_number_value (length),
                                                            false, /* Writable */
                                                            false, /* Enumerable */
                                                            false, /* Configurable */
                                                            false); /* Failure handling */

    JERRY_ASSERT (ecma_is_value_boolean (completion));

    ecma_deref_ecma_string (magic_string_length_p);

    /* 19-21. */
    ecma_object_t *thrower_p = ecma_builtin_get (ECMA_BUILTIN_ID_TYPE_ERROR_THROWER);

    ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor ();
    {
      prop_desc.is_enumerable_defined = true;
      prop_desc.is_enumerable = false;

      prop_desc.is_configurable_defined = true;
      prop_desc.is_configurable = false;

      prop_desc.is_get_defined = true;
      prop_desc.get_p = thrower_p;

      prop_desc.is_set_defined = true;
      prop_desc.set_p = thrower_p;
    }

    ecma_string_t *magic_string_caller_p = ecma_get_magic_string (LIT_MAGIC_STRING_CALLER);
    completion = ecma_op_object_define_own_property (function_p,
                                                     magic_string_caller_p,
                                                     &prop_desc,
                                                     false);

    JERRY_ASSERT (ecma_is_value_boolean (completion));

    ecma_deref_ecma_string (magic_string_caller_p);

    ecma_string_t *magic_string_arguments_p = ecma_get_magic_string (LIT_MAGIC_STRING_ARGUMENTS);
    completion = ecma_op_object_define_own_property (function_p,
                                                     magic_string_arguments_p,
                                                     &prop_desc,
                                                     false);

    JERRY_ASSERT (ecma_is_value_boolean (completion));

    ecma_deref_ecma_string (magic_string_arguments_p);
    ecma_deref_object (thrower_p);

    /* 22. */
    ret_value = ecma_make_object_value (function_p);
  }

  return ret_value;
} /* ecma_builtin_function_prototype_object_bind */
/**
 * The Function.prototype object's 'apply' routine
 *
 * See also:
 *          ECMA-262 v5, 15.3.4.3
 *
 * @return completion value
 *         Returned value must be freed with ecma_free_completion_value.
 */
static ecma_completion_value_t
ecma_builtin_function_prototype_object_apply (ecma_value_t this_arg, /**< this argument */
                                              ecma_value_t arg1, /**< first argument */
                                              ecma_value_t arg2) /**< second argument */
{
  ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();

  /* 1. */
  if (!ecma_op_is_callable (this_arg))
  {
    ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE));
  }
  else
  {
    ecma_object_t *func_obj_p = ecma_get_object_from_value (this_arg);

    /* 2. */
    if (ecma_is_value_null (arg2) || ecma_is_value_undefined (arg2))
    {
      ret_value = ecma_op_function_call (func_obj_p, arg1, NULL);
    }
    else
    {
      /* 3. */
      if (!ecma_is_value_object (arg2))
      {
        ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE));
      }
      else
      {
        ecma_object_t *obj_p = ecma_get_object_from_value (arg2);
        ecma_string_t *length_magic_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH);

        /* 4. */
        ECMA_TRY_CATCH (length_value,
                        ecma_op_object_get (obj_p, length_magic_string_p),
                        ret_value);

        ECMA_OP_TO_NUMBER_TRY_CATCH (length_number,
                                     length_value,
                                     ret_value);

        /* 5. */
        const uint32_t length = ecma_number_to_uint32 (length_number);

        /* 6. */
        ecma_collection_header_t *arg_collection_p = ecma_new_values_collection (NULL, 0, true);

        /* 7. */
        for (uint32_t index = 0; index < length && ecma_is_completion_value_empty (ret_value); index++)
        {
          ecma_string_t *curr_idx_str_p = ecma_new_ecma_string_from_uint32 (index);

          ECMA_TRY_CATCH (get_value,
                          ecma_op_object_get (obj_p, curr_idx_str_p),
                          ret_value);

          ecma_append_to_values_collection (arg_collection_p, get_value, true);

          ECMA_FINALIZE (get_value);
          ecma_deref_ecma_string (curr_idx_str_p);
        }

        JERRY_ASSERT (arg_collection_p->unit_number == length || !ecma_is_completion_value_empty (ret_value));

        if (ecma_is_completion_value_empty (ret_value))
        {
          ret_value = ecma_op_function_call (func_obj_p,
                                             arg1,
                                             arg_collection_p);
        }

        ecma_free_values_collection (arg_collection_p, true);

        ECMA_OP_TO_NUMBER_FINALIZE (length_number);
        ECMA_FINALIZE (length_value);
        ecma_deref_ecma_string (length_magic_string_p);
      }
    }
  }

  return ret_value;
} /* ecma_builtin_function_prototype_object_apply */
/**
 * The JSON object's 'stringify' routine
 *
 * See also:
 *          ECMA-262 v5, 15.12.3
 *
 * @return ecma value
 *         Returned value must be freed with ecma_free_value.
 */
static ecma_value_t
ecma_builtin_json_stringify (ecma_value_t this_arg __attr_unused___, /**< 'this' argument */
                             ecma_value_t arg1,  /**< value */
                             ecma_value_t arg2,  /**< replacer */
                             ecma_value_t arg3)  /**< space */
{
  ecma_value_t ret_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY);

  ecma_json_stringify_context_t context;

  /* 1. */
  context.occurence_stack_p = ecma_new_values_collection (NULL, 0, false);

  /* 2. */
  context.indent_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);

  /* 3. */
  context.property_list_p = ecma_new_values_collection (NULL, 0, false);

  context.replacer_function_p = NULL;

  /* 4. */
  if (ecma_is_value_object (arg2))
  {
    ecma_object_t *obj_p = ecma_get_object_from_value (arg2);

    /* 4.a */
    if (ecma_op_is_callable (arg2))
    {
      context.replacer_function_p = obj_p;
    }
    /* 4.b */
    else if (ecma_object_get_class_name (obj_p) == LIT_MAGIC_STRING_ARRAY_UL)
    {
      ecma_string_t *length_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH);

      ECMA_TRY_CATCH (array_length,
                      ecma_op_object_get (obj_p, length_str_p),
                      ret_value);

      ECMA_OP_TO_NUMBER_TRY_CATCH (array_length_num,
                                   array_length,
                                   ret_value);

      uint32_t array_length = ecma_number_to_uint32 (array_length_num);
      uint32_t index = 0;

      /* 4.b.ii */
      while ((index < array_length) && ecma_is_value_empty (ret_value))
      {
        ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (index);

        ECMA_TRY_CATCH (value,
                        ecma_op_object_get (obj_p, index_str_p),
                        ret_value);

        /* 4.b.ii.1 */
        ecma_value_t item = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);

        /* 4.b.ii.2 */
        if (ecma_is_value_string (value))
        {
          item = ecma_copy_value (value, true);
        }
        /* 4.b.ii.3 */
        else if (ecma_is_value_number (value))
        {
          ECMA_TRY_CATCH (str_val,
                          ecma_op_to_string (value),
                          ret_value);

          item = ecma_copy_value (str_val, true);

          ECMA_FINALIZE (str_val);
        }
        /* 4.b.ii.4 */
        else if (ecma_is_value_object (value))
        {
          ecma_object_t *obj_val_p = ecma_get_object_from_value (value);
          lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_val_p);

          /* 4.b.ii.4.a */
          if (class_name == LIT_MAGIC_STRING_NUMBER_UL
              || class_name == LIT_MAGIC_STRING_STRING_UL)
          {
            ECMA_TRY_CATCH (val,
                            ecma_op_to_string (value),
                            ret_value);

            item = ecma_copy_value (val, true);

            ECMA_FINALIZE (val);
          }
        }

        /* 4.b.ii.5 */
        if (!ecma_is_value_undefined (item))
        {
          if (!ecma_has_string_value_in_collection (context.property_list_p, item))
          {
            ecma_append_to_values_collection (context.property_list_p, item, true);
            ecma_deref_ecma_string (ecma_get_string_from_value (item));
          }
          else
          {
            ecma_free_value (item);
          }
        }

        ECMA_FINALIZE (value);

        ecma_deref_ecma_string (index_str_p);

        index++;
      }

      ECMA_OP_TO_NUMBER_FINALIZE (array_length_num);
      ECMA_FINALIZE (array_length);

      ecma_deref_ecma_string (length_str_p);
    }
  }

  if (ecma_is_value_empty (ret_value))
  {
    ecma_value_t space = ecma_copy_value (arg3, true);

    /* 5. */
    if (ecma_is_value_object (arg3))
    {
      ecma_object_t *obj_p = ecma_get_object_from_value (arg3);
      lit_magic_string_id_t class_name = ecma_object_get_class_name (obj_p);

      /* 5.a */
      if (class_name == LIT_MAGIC_STRING_NUMBER_UL)
      {
        ECMA_TRY_CATCH (val,
                        ecma_op_to_number (arg3),
                        ret_value);

        ecma_free_value (space);
        space = ecma_copy_value (val, true);

        ECMA_FINALIZE (val);
      }
      /* 5.b */
      else if (class_name == LIT_MAGIC_STRING_STRING_UL)
      {
        ECMA_TRY_CATCH (val,
                        ecma_op_to_string (arg3),
                        ret_value);

        ecma_free_value (space);
        space = ecma_copy_value (val, true);

        ECMA_FINALIZE (val);
      }
    }

    if (ecma_is_value_empty (ret_value))
    {
      /* 6. */
      if (ecma_is_value_number (space))
      {
        ECMA_OP_TO_NUMBER_TRY_CATCH (array_length_num,
                                     arg3,
                                     ret_value);

        /* 6.a */
        int32_t num_of_spaces = ecma_number_to_int32 (array_length_num);
        int32_t space = (num_of_spaces > 10) ? 10 : num_of_spaces;

        /* 6.b */
        if (space < 1)
        {
          context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);
        }
        else
        {
          MEM_DEFINE_LOCAL_ARRAY (space_buff, space, char);

          for (int32_t i = 0; i < space; i++)
          {
            space_buff[i] = LIT_CHAR_SP;
          }

          context.gap_str_p = ecma_new_ecma_string_from_utf8 ((lit_utf8_byte_t *) space_buff, (lit_utf8_size_t) space);

          MEM_FINALIZE_LOCAL_ARRAY (space_buff);
        }

        ECMA_OP_TO_NUMBER_FINALIZE (array_length_num);
      }
      /* 7. */
      else if (ecma_is_value_string (space))
      {
        ecma_string_t *space_str_p = ecma_get_string_from_value (space);
        ecma_length_t num_of_chars = ecma_string_get_length (space_str_p);

        if (num_of_chars < 10)
        {
          context.gap_str_p = ecma_copy_or_ref_ecma_string (space_str_p);
        }
        else
        {
          context.gap_str_p = ecma_string_substr (space_str_p, 0, 10);
        }
      }
      /* 8. */
      else
      {
        context.gap_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);
      }
    }

    ecma_free_value (space);

    if (ecma_is_value_empty (ret_value))
    {
      /* 9. */
      ecma_object_t *obj_wrapper_p = ecma_op_create_object_object_noarg ();
      ecma_string_t *empty_str_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);

      /* 10. */
      ecma_value_t put_comp_val = ecma_op_object_put (obj_wrapper_p,
                                                      empty_str_p,
                                                      arg1,
                                                      false);

      JERRY_ASSERT (ecma_is_value_true (put_comp_val));
      ecma_free_value (put_comp_val);

      /* 11. */
      ECMA_TRY_CATCH (str_val,
                      ecma_builtin_json_str (empty_str_p, obj_wrapper_p, &context),
                      ret_value);

      ret_value = ecma_copy_value (str_val, true);

      ECMA_FINALIZE (str_val);

      ecma_deref_object (obj_wrapper_p);
      ecma_deref_ecma_string (empty_str_p);
    }

    ecma_deref_ecma_string (context.gap_str_p);
  }

  ecma_deref_ecma_string (context.indent_str_p);

  ecma_free_values_collection (context.property_list_p, true);
  ecma_free_values_collection (context.occurence_stack_p, true);

  return ret_value;
} /* ecma_builtin_json_stringify */
/**
 * Abstract operation 'JA' defined in 15.12.3
 *
 * See also:
 *          ECMA-262 v5, 15.12.3
 *
 * @return ecma value
 *         Returned value must be freed with ecma_free_value.
 */
static ecma_value_t
ecma_builtin_json_array (ecma_object_t *obj_p, /**< the array object*/
                         ecma_json_stringify_context_t *context_p) /**< context*/
{
  ecma_value_t obj_value = ecma_make_object_value (obj_p);

  /* 1. */
  if (ecma_has_object_value_in_collection (context_p->occurence_stack_p, obj_value))
  {
    return ecma_raise_type_error ("");
  }

  ecma_value_t ret_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY);

  /* 2. */
  ecma_append_to_values_collection (context_p->occurence_stack_p, obj_value, true);

  /* 3. */
  ecma_string_t *stepback_p = context_p->indent_str_p;

  /* 4. */
  context_p->indent_str_p = ecma_concat_ecma_strings (context_p->indent_str_p, context_p->gap_str_p);

  /* 5. */
  ecma_collection_header_t *partial_p = ecma_new_values_collection (NULL, 0, true);

  ecma_string_t *length_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH);

  /* 6. */
  ECMA_TRY_CATCH (array_length,
                  ecma_op_object_get (obj_p, length_str_p),
                  ret_value);

  ECMA_OP_TO_NUMBER_TRY_CATCH (array_length_num,
                               array_length,
                               ret_value);

  uint32_t array_length = ecma_number_to_uint32 (array_length_num);

  /* 7. - 8. */
  for (uint32_t index = 0;
       index < array_length && ecma_is_value_empty (ret_value);
       index++)
  {

    /* 8.a */
    ecma_string_t *index_str_p = ecma_new_ecma_string_from_uint32 (index);

    ECMA_TRY_CATCH (str_val,
                    ecma_builtin_json_str (index_str_p, obj_p, context_p),
                    ret_value);

    /* 8.b */
    if (ecma_is_value_undefined (str_val))
    {
      ecma_string_t *null_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_NULL);
      ecma_append_to_values_collection (partial_p, ecma_make_string_value (null_str_p), true);
      ecma_deref_ecma_string (null_str_p);
    }
    /* 8.c */
    else
    {
      ecma_append_to_values_collection (partial_p, str_val, true);
    }

    ECMA_FINALIZE (str_val);
    ecma_deref_ecma_string (index_str_p);
  }

  if (ecma_is_value_empty (ret_value))
  {
    /* 9. */
    if (partial_p->unit_number == 0)
    {
      ecma_string_t *left_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_SQUARE_CHAR);
      ecma_string_t *right_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR);

      ecma_string_t *final_str_p = ecma_concat_ecma_strings (left_square_str_p, right_square_str_p);
      ret_value = ecma_make_string_value (final_str_p);

      ecma_deref_ecma_string (left_square_str_p);
      ecma_deref_ecma_string (right_square_str_p);
    }
    /* 10. */
    else
    {
      bool is_gap_empty = (ecma_string_get_length (context_p->gap_str_p) == 0);
      /* 10.a */
      if (is_gap_empty)
      {
        ecma_string_t *left_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_SQUARE_CHAR);
        ecma_string_t *right_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR);

        ret_value = ecma_builtin_helper_json_create_non_formatted_json (left_square_str_p,
                                                                        right_square_str_p,
                                                                        partial_p);

        ecma_deref_ecma_string (left_square_str_p);
        ecma_deref_ecma_string (right_square_str_p);
      }
      /* 10.b */
      else
      {
        ecma_string_t *left_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_SQUARE_CHAR);
        ecma_string_t *right_square_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_SQUARE_CHAR);

        ret_value = ecma_builtin_helper_json_create_formatted_json (left_square_str_p,
                                                                    right_square_str_p,
                                                                    stepback_p,
                                                                    partial_p,
                                                                    context_p);

        ecma_deref_ecma_string (left_square_str_p);
        ecma_deref_ecma_string (right_square_str_p);
      }
    }
  }

  ECMA_OP_TO_NUMBER_FINALIZE (array_length_num);
  ECMA_FINALIZE (array_length);

  ecma_deref_ecma_string (length_str_p);
  ecma_free_values_collection (partial_p, true);

  /* 11. */
  ecma_remove_last_value_from_values_collection (context_p->occurence_stack_p);

  /* 12. */
  ecma_deref_ecma_string (context_p->indent_str_p);
  context_p->indent_str_p = stepback_p;

  /* 13. */
  return ret_value;
} /* ecma_builtin_json_array */
/**
 * Abstract operation 'JO' defined in 15.12.3
 *
 * See also:
 *          ECMA-262 v5, 15.12.3
 *
 * @return ecma value
 *         Returned value must be freed with ecma_free_value.
 */
static ecma_value_t
ecma_builtin_json_object (ecma_object_t *obj_p, /**< the object*/
                          ecma_json_stringify_context_t *context_p) /**< context*/
{
  ecma_value_t obj_value = ecma_make_object_value (obj_p);

  /* 1. */
  if (ecma_has_object_value_in_collection (context_p->occurence_stack_p, obj_value))
  {
    return ecma_raise_type_error ("");
  }

  ecma_value_t ret_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY);

  /* 2. */
  ecma_append_to_values_collection (context_p->occurence_stack_p, obj_value, true);

  /* 3. */
  ecma_string_t *stepback_p = context_p->indent_str_p;

  /* 4. */
  context_p->indent_str_p = ecma_concat_ecma_strings (context_p->indent_str_p, context_p->gap_str_p);

  ecma_collection_header_t *property_keys_p;

  /* 5. */
  if (context_p->property_list_p->unit_number > 0)
  {
    property_keys_p = context_p->property_list_p;
  }
  /* 6. */
  else
  {
    property_keys_p = ecma_new_values_collection (NULL, 0, true);

    ecma_collection_header_t *props_p = ecma_op_object_get_property_names (obj_p, false, true, false);

    ecma_collection_iterator_t iter;
    ecma_collection_iterator_init (&iter, props_p);

    while (ecma_collection_iterator_next (&iter))
    {
      ecma_string_t *property_name_p = ecma_get_string_from_value (*iter.current_value_p);
      ecma_property_t *property_p = ecma_op_object_get_own_property (obj_p, property_name_p);

      JERRY_ASSERT (ecma_is_property_enumerable (property_p));

      if (property_p->type == ECMA_PROPERTY_NAMEDDATA)
      {
        ecma_append_to_values_collection (property_keys_p, *iter.current_value_p, true);
      }
    }

    ecma_free_values_collection (props_p, true);
  }

  /* 7. */
  ecma_collection_header_t *partial_p = ecma_new_values_collection (NULL, 0, true);

  /* 8. */
  ecma_collection_iterator_t iterator;
  ecma_collection_iterator_init (&iterator, property_keys_p);

  while (ecma_collection_iterator_next (&iterator) && ecma_is_value_empty (ret_value))
  {
    ecma_value_t value = *iterator.current_value_p;
    ecma_string_t *key_p = ecma_get_string_from_value (value);

    /* 8.a */
    ECMA_TRY_CATCH (str_val,
                    ecma_builtin_json_str (key_p, obj_p, context_p),
                    ret_value);

    /* 8.b */
    if (!ecma_is_value_undefined (str_val))
    {
      ecma_string_t *colon_p = ecma_get_magic_string (LIT_MAGIC_STRING_COLON_CHAR);
      ecma_string_t *value_str_p = ecma_get_string_from_value (str_val);
      ecma_string_t *tmp_str_p;

      /* 8.b.i */
      ecma_value_t str_comp_val = ecma_builtin_json_quote (key_p);
      JERRY_ASSERT (!ecma_is_value_error (str_comp_val));

      ecma_string_t *member_str_p = ecma_get_string_from_value (str_comp_val);

      /* 8.b.ii */
      tmp_str_p = ecma_concat_ecma_strings (member_str_p, colon_p);
      ecma_free_value (str_comp_val);
      ecma_deref_ecma_string (colon_p);
      member_str_p = tmp_str_p;

      /* 8.b.iii */
      bool is_gap_empty = (ecma_string_get_length (context_p->gap_str_p) == 0);

      if (!is_gap_empty)
      {
        ecma_string_t *space_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_SPACE_CHAR);

        tmp_str_p = ecma_concat_ecma_strings (member_str_p, space_str_p);
        ecma_deref_ecma_string (member_str_p);
        ecma_deref_ecma_string (space_str_p);
        member_str_p = tmp_str_p;
      }

      /* 8.b.iv */
      tmp_str_p = ecma_concat_ecma_strings (member_str_p, value_str_p);
      ecma_deref_ecma_string (member_str_p);
      member_str_p = tmp_str_p;

      /* 8.b.v */
      ecma_value_t member_value = ecma_make_string_value (member_str_p);
      ecma_append_to_values_collection (partial_p, member_value, true);
      ecma_deref_ecma_string (member_str_p);
    }

    ECMA_FINALIZE (str_val);
  }

  if (context_p->property_list_p->unit_number == 0)
  {
    ecma_free_values_collection (property_keys_p, true);
  }

  if (!ecma_is_value_empty (ret_value))
  {
    ecma_free_values_collection (partial_p, true);
    ecma_deref_ecma_string (stepback_p);
    return ret_value;
  }

  /* 9. */
  if (partial_p->unit_number == 0)
  {
    ecma_string_t *left_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_BRACE_CHAR);
    ecma_string_t *right_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_BRACE_CHAR);

    ecma_string_t *final_str_p = ecma_concat_ecma_strings (left_brace_str_p, right_brace_str_p);
    ret_value = ecma_make_string_value (final_str_p);

    ecma_deref_ecma_string (left_brace_str_p);
    ecma_deref_ecma_string (right_brace_str_p);
  }
  /* 10. */
  else
  {
    bool is_gap_empty = (ecma_string_get_length (context_p->gap_str_p) == 0);

    /* 10.a */
    if (is_gap_empty)
    {
      ecma_string_t *left_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_BRACE_CHAR);
      ecma_string_t *right_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_BRACE_CHAR);

      ret_value = ecma_builtin_helper_json_create_non_formatted_json (left_brace_str_p,
                                                                      right_brace_str_p,
                                                                      partial_p);

      ecma_deref_ecma_string (left_brace_str_p);
      ecma_deref_ecma_string (right_brace_str_p);
    }
    /* 10.b */
    else
    {
      ecma_string_t *left_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_LEFT_BRACE_CHAR);
      ecma_string_t *right_brace_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_RIGHT_BRACE_CHAR);

      ret_value = ecma_builtin_helper_json_create_formatted_json (left_brace_str_p,
                                                                  right_brace_str_p,
                                                                  stepback_p,
                                                                  partial_p,
                                                                  context_p);

      ecma_deref_ecma_string (left_brace_str_p);
      ecma_deref_ecma_string (right_brace_str_p);
    }
  }

  ecma_free_values_collection (partial_p, true);

  /* 11. */
  ecma_remove_last_value_from_values_collection (context_p->occurence_stack_p);

  /* 12. */
  ecma_deref_ecma_string (context_p->indent_str_p);
  context_p->indent_str_p = stepback_p;

  /* 13. */
  return ret_value;
} /* ecma_builtin_json_object */