diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.c index 2da23c79a..2cbd0e760 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.c @@ -102,6 +102,72 @@ ecma_builtin_string_prototype_object_value_of (ecma_value_t this_arg) /**< this return ecma_builtin_string_prototype_object_to_string (this_arg); } /* ecma_builtin_string_prototype_object_value_of */ +/** + * Helper function for the String.prototype object's 'charAt' and charCodeAt' routine + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_string_prototype_char_at_helper (ecma_value_t this_arg, /**< this argument */ + ecma_value_t arg, /**< routine's argument */ + bool charcode_mode) /**< routine mode */ +{ + /* 1 */ + ecma_value_t check_coercible_val = ecma_op_check_object_coercible (this_arg); + + if (ECMA_IS_VALUE_ERROR (check_coercible_val)) + { + return check_coercible_val; + } + ecma_free_value (check_coercible_val); + + /* 3 */ + ecma_number_t index_num; + ecma_value_t to_num_result = ecma_get_number (arg, &index_num); + + if (JERRY_UNLIKELY (!ecma_is_value_empty (to_num_result))) + { + return to_num_result; + } + ecma_free_value (to_num_result); + + /* 2 */ + ecma_value_t to_string_val = ecma_op_to_string (this_arg); + if (ECMA_IS_VALUE_ERROR (to_string_val)) + { + return to_string_val; + } + + /* 4 */ + ecma_string_t *original_string_p = ecma_get_string_from_value (to_string_val); + const ecma_length_t len = ecma_string_get_length (original_string_p); + + /* 5 */ + // When index_num is NaN, then the first two comparisons are false + if (index_num < 0 || index_num >= len || (ecma_number_is_nan (index_num) && len == 0)) + { + ecma_free_value (to_string_val); + return (charcode_mode ? ecma_make_nan_value () + : ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY)); + } + + /* 6 */ + /* + * String length is currently uint32_t, but index_num may be bigger, + * ToInteger performs floor, while ToUInt32 performs modulo 2^32, + * hence after the check 0 <= index_num < len we assume to_uint32 can be used. + * We assume to_uint32 (NaN) is 0. + */ + JERRY_ASSERT (ecma_number_is_nan (index_num) || ecma_number_to_uint32 (index_num) == ecma_number_trunc (index_num)); + + ecma_char_t new_ecma_char = ecma_string_get_char_at_pos (original_string_p, ecma_number_to_uint32 (index_num)); + ecma_free_value (to_string_val); + + return (charcode_mode ? ecma_make_uint32_value (new_ecma_char) + : ecma_make_string_value (ecma_new_ecma_string_from_code_unit (new_ecma_char))); +} /* ecma_builtin_string_prototype_char_at_helper */ + /** * The String.prototype object's 'charAt' routine * @@ -115,45 +181,7 @@ static ecma_value_t ecma_builtin_string_prototype_object_char_at (ecma_value_t this_arg, /**< this argument */ ecma_value_t arg) /**< routine's argument */ { - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - - /* 1 */ - ECMA_TRY_CATCH (check_coercible_val, - ecma_op_check_object_coercible (this_arg), - ret_value); - - /* 2 */ - ECMA_TRY_CATCH (to_string_val, - ecma_op_to_string (this_arg), - ret_value); - - /* 3 */ - ECMA_OP_TO_NUMBER_TRY_CATCH (index_num, - arg, - ret_value); - - /* 4 */ - ecma_string_t *original_string_p = ecma_get_string_from_value (to_string_val); - const ecma_length_t len = ecma_string_get_length (original_string_p); - - /* 5 */ - if (index_num < 0 || index_num >= len || !len) - { - ret_value = ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY); - } - else - { - /* 6 */ - ecma_char_t new_ecma_char = ecma_string_get_char_at_pos (original_string_p, ecma_number_to_uint32 (index_num)); - ret_value = ecma_make_string_value (ecma_new_ecma_string_from_code_unit (new_ecma_char)); - } - - ECMA_OP_TO_NUMBER_FINALIZE (index_num); - - ECMA_FINALIZE (to_string_val); - ECMA_FINALIZE (check_coercible_val); - - return ret_value; + return ecma_builtin_string_prototype_char_at_helper (this_arg, arg, false); } /* ecma_builtin_string_prototype_object_char_at */ /** @@ -169,54 +197,7 @@ static ecma_value_t ecma_builtin_string_prototype_object_char_code_at (ecma_value_t this_arg, /**< this argument */ ecma_value_t arg) /**< routine's argument */ { - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - - /* 1 */ - ECMA_TRY_CATCH (check_coercible_val, - ecma_op_check_object_coercible (this_arg), - ret_value); - - /* 2 */ - ECMA_TRY_CATCH (to_string_val, - ecma_op_to_string (this_arg), - ret_value); - - /* 3 */ - ECMA_OP_TO_NUMBER_TRY_CATCH (index_num, - arg, - ret_value); - - /* 4 */ - ecma_string_t *original_string_p = ecma_get_string_from_value (to_string_val); - const ecma_length_t len = ecma_string_get_length (original_string_p); - - /* 5 */ - // When index_num is NaN, then the first two comparisons are false - if (index_num < 0 || index_num >= len || (ecma_number_is_nan (index_num) && !len)) - { - ret_value = ecma_make_nan_value (); - } - else - { - /* 6 */ - /* - * String length is currently uit32_t, but index_num may be bigger, - * ToInteger performs floor, while ToUInt32 performs modulo 2^32, - * hence after the check 0 <= index_num < len we assume to_uint32 can be used. - * We assume to_uint32 (NaN) is 0. - */ - JERRY_ASSERT (ecma_number_is_nan (index_num) || ecma_number_to_uint32 (index_num) == ecma_number_trunc (index_num)); - - ecma_char_t new_ecma_char = ecma_string_get_char_at_pos (original_string_p, ecma_number_to_uint32 (index_num)); - ret_value = ecma_make_uint32_value (new_ecma_char); - } - - ECMA_OP_TO_NUMBER_FINALIZE (index_num); - - ECMA_FINALIZE (to_string_val); - ECMA_FINALIZE (check_coercible_val); - - return ret_value; + return ecma_builtin_string_prototype_char_at_helper (this_arg, arg, true); } /* ecma_builtin_string_prototype_object_char_code_at */ /**