/** * ToString operation. * * See also: * ECMA-262 v5, 9.8 * * @return completion value * Returned value must be freed with ecma_free_completion_value */ ecma_completion_value_t ecma_op_to_string (ecma_value_t value) /**< ecma-value */ { ecma_check_value_type_is_spec_defined (value); if (unlikely (ecma_is_value_object (value))) { ecma_completion_value_t ret_value = ecma_make_empty_completion_value (); ECMA_TRY_CATCH (prim_value, ecma_op_to_primitive (value, ECMA_PREFERRED_TYPE_STRING), ret_value); ret_value = ecma_op_to_string (prim_value); ECMA_FINALIZE (prim_value); return ret_value; } else { ecma_string_t *res_p = NULL; if (ecma_is_value_string (value)) { res_p = ecma_get_string_from_value (value); res_p = ecma_copy_or_ref_ecma_string (res_p); } else if (ecma_is_value_number (value)) { ecma_number_t *num_p = ecma_get_number_from_value (value); res_p = ecma_new_ecma_string_from_number (*num_p); } else if (ecma_is_value_undefined (value)) { res_p = ecma_get_magic_string (LIT_MAGIC_STRING_UNDEFINED); } else if (ecma_is_value_null (value)) { res_p = ecma_get_magic_string (LIT_MAGIC_STRING_NULL); } else { JERRY_ASSERT (ecma_is_value_boolean (value)); if (ecma_is_value_true (value)) { res_p = ecma_get_magic_string (LIT_MAGIC_STRING_TRUE); } else { res_p = ecma_get_magic_string (LIT_MAGIC_STRING_FALSE); } } return ecma_make_normal_completion_value (ecma_make_string_value (res_p)); } } /* ecma_op_to_string */
/** * The Number.prototype object's 'toString' routine * * See also: * ECMA-262 v5, 15.7.4.2 * * @return ecma value * Returned value must be freed with ecma_free_value. */ static ecma_value_t ecma_builtin_number_prototype_object_to_string (ecma_value_t this_arg, /**< this argument */ const ecma_value_t *arguments_list_p, /**< arguments list */ ecma_length_t arguments_list_len) /**< number of arguments */ { ecma_value_t ret_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY); ECMA_TRY_CATCH (this_value, ecma_builtin_number_prototype_object_value_of (this_arg), ret_value); ecma_number_t this_arg_number = ecma_get_number_from_value (this_value); if (arguments_list_len == 0 || ecma_number_is_nan (this_arg_number) || ecma_number_is_infinity (this_arg_number) || ecma_number_is_zero (this_arg_number) || (arguments_list_len > 0 && ecma_is_value_undefined (arguments_list_p[0]))) { ecma_string_t *ret_str_p = ecma_new_ecma_string_from_number (this_arg_number); ret_value = ecma_make_string_value (ret_str_p); } else { static const lit_utf8_byte_t digit_chars[36] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; ECMA_OP_TO_NUMBER_TRY_CATCH (arg_num, arguments_list_p[0], ret_value); uint32_t radix = ecma_number_to_uint32 (arg_num); if (radix < 2 || radix > 36) { ret_value = ecma_raise_range_error (ECMA_ERR_MSG ("")); } else if (radix == 10) { ecma_string_t *ret_str_p = ecma_new_ecma_string_from_number (this_arg_number); ret_value = ecma_make_string_value (ret_str_p); } else { bool is_negative = false; if (ecma_number_is_negative (this_arg_number)) { this_arg_number = -this_arg_number; is_negative = true; } lit_utf8_byte_t digits[ECMA_MAX_CHARS_IN_STRINGIFIED_NUMBER]; int32_t exponent; lit_utf8_size_t num_digits = ecma_number_to_decimal (this_arg_number, digits, &exponent); exponent = exponent - (int32_t) num_digits; /* Calculate the scale of the number in the specified radix. */ int scale = (int) -floor ((log (10) / log (radix)) * exponent); bool is_scale_negative = false; if (scale < 0) { is_scale_negative = true; scale = -scale; } int buff_size; if (is_scale_negative) { buff_size = (int) floor (log (this_arg_number) / log (radix)) + 1; } else { buff_size = scale + ECMA_NUMBER_FRACTION_WIDTH + 2; } if (is_negative) { buff_size++; } /* Normalize the number, so that it is as close to 0 exponent as possible. */ if (is_scale_negative) { for (int i = 0; i < scale; i++) { this_arg_number /= (ecma_number_t) radix; } } else { for (int i = 0; i < scale; i++) { this_arg_number *= (ecma_number_t) radix; } } uint64_t whole = (uint64_t) this_arg_number; ecma_number_t fraction = this_arg_number - (ecma_number_t) whole; bool should_round = false; if (!ecma_number_is_zero (fraction) && is_scale_negative) { /* Add one extra digit for rounding. */ buff_size++; should_round = true; } JMEM_DEFINE_LOCAL_ARRAY (buff, buff_size, lit_utf8_byte_t); int buff_index = 0; /* Calculate digits for whole part. */ while (whole > 0) { buff[buff_index++] = (lit_utf8_byte_t) (whole % radix); whole /= radix; } /* Calculate where we have to put the radix point. */ int point = is_scale_negative ? buff_index + scale : buff_index - scale; /* Reverse the digits, since they are backwards. */ for (int i = 0; i < buff_index / 2; i++) { lit_utf8_byte_t swap = buff[i]; buff[i] = buff[buff_index - i - 1]; buff[buff_index - i - 1] = swap; } int required_digits = buff_size; if (is_negative) { required_digits--; } if (!is_scale_negative) { /* Leave space for leading zeros / radix point. */ required_digits -= scale + 1; } /* Calculate digits for fractional part. */ while (buff_index < required_digits && (fraction != 0 || is_scale_negative)) { fraction *= (ecma_number_t) radix; lit_utf8_byte_t digit = (lit_utf8_byte_t) floor (fraction); buff[buff_index++] = digit; fraction -= (ecma_number_t) floor (fraction); } if (should_round) { /* Round off last digit. */ if (buff[buff_index - 1] > radix / 2) { buff[buff_index - 2]++; } buff_index--; /* Propagate carry. */ for (int i = buff_index - 1; i > 0 && buff[i] >= radix; i--) { buff[i] = (lit_utf8_byte_t) (buff[i] - radix); buff[i - 1]++; } /* Carry propagated over the whole number, need to add a leading digit. */ if (buff[0] >= radix) { memmove (buff + 1, buff, (size_t) buff_index); buff_index++; buff[0] = 1; } } /* Remove trailing zeros from fraction. */ while (buff_index - 1 > point && buff[buff_index - 1] == 0) { buff_index--; } /* Add leading zeros in case place of radix point is negative. */ if (point <= 0) { memmove (buff - point + 1, buff, (size_t) buff_index); buff_index += -point + 1; for (int i = 0; i < -point + 1; i++) { buff[i] = 0; } point = 1; } /* Convert digits to characters. */ for (int i = 0; i < buff_index; i++) { buff[i] = digit_chars[buff[i]]; } /* Place radix point to the required position. */ if (point < buff_index) { memmove (buff + point + 1, buff + point, (size_t) buff_index); buff[point] = '.'; buff_index++; } /* Add negative sign if necessary. */ if (is_negative) { memmove (buff + 1, buff, (size_t) buff_index); buff_index++; buff[0] = '-'; } JERRY_ASSERT (buff_index <= buff_size); ecma_string_t *str_p = ecma_new_ecma_string_from_utf8 (buff, (lit_utf8_size_t) buff_index); ret_value = ecma_make_string_value (str_p); JMEM_FINALIZE_LOCAL_ARRAY (buff); } ECMA_OP_TO_NUMBER_FINALIZE (arg_num); } ECMA_FINALIZE (this_value); return ret_value; } /* ecma_builtin_number_prototype_object_to_string */
/** * ToString operation. * * See also: * ECMA-262 v5, 9.8 * * @return ecma value * Returned value must be freed with ecma_free_value */ ecma_value_t ecma_op_to_string (ecma_value_t value) /**< ecma value */ { ecma_check_value_type_is_spec_defined (value); if (unlikely (ecma_is_value_object (value))) { ecma_value_t ret_value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_EMPTY); ECMA_TRY_CATCH (prim_value, ecma_op_to_primitive (value, ECMA_PREFERRED_TYPE_STRING), ret_value); ret_value = ecma_op_to_string (prim_value); ECMA_FINALIZE (prim_value); return ret_value; } else { ecma_string_t *res_p = NULL; if (ecma_is_value_string (value)) { res_p = ecma_get_string_from_value (value); res_p = ecma_copy_or_ref_ecma_string (res_p); } else if (ecma_is_value_integer_number (value)) { ecma_integer_value_t num = ecma_get_integer_from_value (value); if (num < 0) { res_p = ecma_new_ecma_string_from_number ((ecma_number_t) num); } else { res_p = ecma_new_ecma_string_from_uint32 ((uint32_t) num); } } else if (ecma_is_value_float_number (value)) { ecma_number_t num = ecma_get_float_from_value (value); res_p = ecma_new_ecma_string_from_number (num); } else if (ecma_is_value_undefined (value)) { res_p = ecma_get_magic_string (LIT_MAGIC_STRING_UNDEFINED); } else if (ecma_is_value_null (value)) { res_p = ecma_get_magic_string (LIT_MAGIC_STRING_NULL); } else { JERRY_ASSERT (ecma_is_value_boolean (value)); if (ecma_is_value_true (value)) { res_p = ecma_get_magic_string (LIT_MAGIC_STRING_TRUE); } else { res_p = ecma_get_magic_string (LIT_MAGIC_STRING_FALSE); } } return ecma_make_string_value (res_p); } } /* ecma_op_to_string */