Implement String.prototype.replace function.

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg@inf.u-szeged.hu
This commit is contained in:
Zoltan Herczeg 2015-07-29 04:45:04 -07:00
parent 39cf5aaa7c
commit 048e20925e
4 changed files with 845 additions and 5 deletions

View File

@ -20,6 +20,7 @@
#include "ecma-builtins.h"
#include "ecma-conversion.h"
#include "ecma-exceptions.h"
#include "ecma-function-object.h"
#include "ecma-gc.h"
#include "ecma-globals.h"
#include "ecma-helpers.h"
@ -743,11 +744,675 @@ ecma_builtin_string_prototype_object_match (ecma_value_t this_arg, /**< this arg
return ret_value;
} /* ecma_builtin_string_prototype_object_match */
#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */
/**
* This structure is the context which represents
* the state of the ongoing string replace.
*/
typedef struct
{
/* General part. */
bool is_regexp; /**< whether we search a regexp or string */
bool is_global; /**< global search or not */
bool is_replace_callable; /**< replace part is callable or not */
ecma_value_t input_string; /**< input string */
ecma_length_t input_length; /**< input string length */
ecma_value_t regexp_or_search_string; /**< regular expression or search string
* depending on the value of is_regexp */
ecma_length_t match_start; /**< starting position of the match */
ecma_length_t match_end; /**< end position of the match */
/* Replace value callable part. */
ecma_object_t *replace_function_p;
/* Replace value string part. */
ecma_string_t *replace_string_p; /**< replace string */
lit_utf8_iterator_t replace_iterator; /**< replace string iterator */
} ecma_builtin_replace_search_ctx_t;
/**
* Generic helper function to append a substring at the end of a base string
*
* The base string can be kept or freed
*
* @return the constructed string
*/
static ecma_string_t *
ecma_builtin_string_prototype_object_replace_append_substr (ecma_string_t *base_string_p, /**< base string */
ecma_string_t *appended_string_p, /**< appended string */
ecma_length_t start, /**< start position */
ecma_length_t end, /**< end position */
bool free_base_string) /**< free base string or not */
{
ecma_string_t *ret_string_p;
JERRY_ASSERT (start <= end);
JERRY_ASSERT (end <= ecma_string_get_length (appended_string_p));
if (start < end)
{
ecma_string_t *substring_p = ecma_string_substr (appended_string_p, start, end);
ret_string_p = ecma_concat_ecma_strings (base_string_p, substring_p);
ecma_deref_ecma_string (substring_p);
if (free_base_string)
{
ecma_deref_ecma_string (base_string_p);
}
}
else if (free_base_string)
{
ret_string_p = base_string_p;
}
else
{
ret_string_p = ecma_copy_or_ref_ecma_string (base_string_p);
}
return ret_string_p;
} /* ecma_builtin_string_prototype_object_replace_append_substr */
/**
* Generic helper function to perform the find the next match
*
* @return completion value
* Returned value must be freed with ecma_free_completion_value.
*/
static ecma_completion_value_t
ecma_builtin_string_prototype_object_replace_match (ecma_builtin_replace_search_ctx_t *context_p) /**< search
* context */
{
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
context_p->match_start = 0;
context_p->match_end = 0;
if (context_p->is_regexp)
{
ecma_value_t exec_arguments[1] = { context_p->input_string };
ECMA_TRY_CATCH (match_value,
ecma_builtin_regexp_prototype_dispatch_routine (LIT_MAGIC_STRING_EXEC,
context_p->regexp_or_search_string,
exec_arguments,
1),
ret_value);
if (!ecma_is_value_null (match_value))
{
JERRY_ASSERT (ecma_is_value_object (match_value));
ecma_object_t *match_object_p = ecma_get_object_from_value (match_value);
ecma_string_t *index_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_INDEX);
ecma_string_t *zero_string_p = ecma_new_ecma_string_from_uint32 (0);
ECMA_TRY_CATCH (index_value,
ecma_op_object_get (match_object_p, index_string_p),
ret_value);
ECMA_TRY_CATCH (result_string_value,
ecma_op_object_get (match_object_p, zero_string_p),
ret_value);
/* We directly call the built-in exec, so
* we can trust in the returned value. */
JERRY_ASSERT (ecma_is_value_number (index_value));
JERRY_ASSERT (ecma_is_value_string (result_string_value));
/* We use the length of the result string to determine the
* match end. This works regardless the global flag is set. */
ecma_string_t *result_string_p = ecma_get_string_from_value (result_string_value);
ecma_number_t *index_number_p = ecma_get_number_from_value (index_value);
context_p->match_start = (ecma_length_t) (*index_number_p);
context_p->match_end = context_p->match_start + (ecma_length_t) ecma_string_get_length (result_string_p);
JERRY_ASSERT ((ecma_length_t) ecma_number_to_uint32 (*index_number_p) == context_p->match_start);
ret_value = ecma_make_normal_completion_value (ecma_copy_value (match_value, true));
ECMA_FINALIZE (result_string_value);
ECMA_FINALIZE (index_value);
ecma_deref_ecma_string (index_string_p);
ecma_deref_ecma_string (zero_string_p);
}
else
{
ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_NULL);
}
ECMA_FINALIZE (match_value);
}
else
{
JERRY_ASSERT (!context_p->is_global);
ecma_string_t *search_string_p = ecma_get_string_from_value (context_p->regexp_or_search_string);
lit_utf8_size_t search_size = ecma_string_get_size (search_string_p);
MEM_DEFINE_LOCAL_ARRAY (search_start_p,
search_size,
lit_utf8_byte_t);
ecma_string_to_utf8_string (search_string_p,
search_start_p,
(ssize_t) (search_size));
ecma_string_t *input_string_p = ecma_get_string_from_value (context_p->input_string);
lit_utf8_size_t input_size = ecma_string_get_size (input_string_p);
MEM_DEFINE_LOCAL_ARRAY (input_start_p,
input_size,
lit_utf8_byte_t);
ecma_string_to_utf8_string (input_string_p,
input_start_p,
(ssize_t) (input_size));
lit_utf8_iterator_t search_iterator = lit_utf8_iterator_create (search_start_p, search_size);
lit_utf8_iterator_t input_iterator = lit_utf8_iterator_create (input_start_p, input_size);
ecma_length_t match_start = 0;
ecma_length_t match_end = 0;
bool match_found = false;
if (lit_utf8_iterator_is_eos (&search_iterator))
{
/* Empty string, always matches. */
match_found = true;
}
else
{
ecma_char_t first_char = lit_utf8_iterator_read_next (&search_iterator);
while (!lit_utf8_iterator_is_eos (&input_iterator))
{
if (lit_utf8_iterator_read_next (&input_iterator) == first_char)
{
/* Local copy to preserve the original value of the iterators. */
lit_utf8_iterator_t nested_search_iterator = search_iterator;
lit_utf8_iterator_t nested_input_iterator = input_iterator;
match_end = match_start + 1;
match_found = true;
while (!lit_utf8_iterator_is_eos (&nested_search_iterator))
{
if (lit_utf8_iterator_is_eos (&nested_input_iterator))
{
match_found = false;
break;
}
ecma_char_t search_character = lit_utf8_iterator_read_next (&nested_search_iterator);
ecma_char_t input_character = lit_utf8_iterator_read_next (&nested_input_iterator);
if (search_character != input_character)
{
match_found = false;
break;
}
match_end++;
}
if (match_found)
{
break;
}
}
match_start++;
}
}
if (match_found)
{
ecma_value_t arguments_list_p[1] = { context_p->regexp_or_search_string };
ECMA_TRY_CATCH (new_array_value,
ecma_op_create_array_object (arguments_list_p, 1, false),
ret_value);
context_p->match_start = match_start;
context_p->match_end = match_end;
ret_value = ecma_make_normal_completion_value (ecma_copy_value (new_array_value, true));
ECMA_FINALIZE (new_array_value);
}
else
{
ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_NULL);
}
MEM_FINALIZE_LOCAL_ARRAY (input_start_p);
MEM_FINALIZE_LOCAL_ARRAY (search_start_p);
}
return ret_value;
} /* ecma_builtin_string_prototype_object_replace_match */
/**
* Generic helper function to construct the string which replaces the matched part
*
* @return completion value
* Returned value must be freed with ecma_free_completion_value.
*/
static ecma_completion_value_t
ecma_builtin_string_prototype_object_replace_get_string (ecma_builtin_replace_search_ctx_t *context_p, /**< search
* context */
ecma_value_t match_value) /**< returned match value */
{
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
if (context_p->is_replace_callable)
{
ecma_object_t *match_object_p = ecma_get_object_from_value (match_value);
ecma_string_t *length_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH);
ECMA_TRY_CATCH (length_value,
ecma_op_object_get (match_object_p, length_string_p),
ret_value);
JERRY_ASSERT (ecma_is_value_number (length_value));
ecma_number_t *length_number_p = ecma_get_number_from_value (length_value);
ecma_length_t length = (ecma_length_t) (*length_number_p);
JERRY_ASSERT ((ecma_length_t) ecma_number_to_uint32 (*length_number_p) == length);
JERRY_ASSERT (length >= 1);
MEM_DEFINE_LOCAL_ARRAY (arguments_list,
length + 2,
ecma_value_t);
/* An error might occure during the array copy and
* uninitalized elements must not be freed. */
ecma_length_t values_copied = 0;
for (ecma_length_t i = 0;
(i < length) && ecma_is_completion_value_empty (ret_value);
i++)
{
ecma_string_t *index_p = ecma_new_ecma_string_from_uint32 (i);
ECMA_TRY_CATCH (current_value,
ecma_op_object_get (match_object_p, index_p),
ret_value);
arguments_list[i] = ecma_copy_value (current_value, true);
values_copied++;
ECMA_FINALIZE (current_value);
ecma_deref_ecma_string (index_p);
}
if (ecma_is_completion_value_empty (ret_value))
{
ecma_number_t *index_number_p = ecma_alloc_number ();
*index_number_p = context_p->match_start;
arguments_list[length] = ecma_make_number_value (index_number_p);
arguments_list[length + 1] = ecma_copy_value (context_p->input_string, true);
ECMA_TRY_CATCH (result_value,
ecma_op_function_call (context_p->replace_function_p,
context_p->regexp_or_search_string,
arguments_list,
length + 2),
ret_value);
ECMA_TRY_CATCH (to_string_value,
ecma_op_to_string (result_value),
ret_value);
ret_value = ecma_make_normal_completion_value (ecma_copy_value (to_string_value, true));
ECMA_FINALIZE (to_string_value);
ECMA_FINALIZE (result_value);
ecma_free_value (arguments_list[length + 1], true);
ecma_dealloc_number (index_number_p);
}
for (ecma_length_t i = 0; i < values_copied; i++)
{
ecma_free_value (arguments_list[i], true);
}
MEM_FINALIZE_LOCAL_ARRAY (arguments_list);
ECMA_FINALIZE (length_value);
ecma_deref_ecma_string (length_string_p);
}
else
{
ecma_string_t *result_string_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);
ecma_length_t previous_start = 0;
ecma_length_t current_position = 0;
lit_utf8_iterator_t replace_iterator = context_p->replace_iterator;
JERRY_ASSERT (lit_utf8_iterator_is_bos (&replace_iterator));
while (!lit_utf8_iterator_is_eos (&replace_iterator))
{
ecma_char_t action = LIT_CHAR_NULL;
if (lit_utf8_iterator_read_next (&replace_iterator) == LIT_CHAR_DOLLAR_SIGN)
{
if (!lit_utf8_iterator_is_eos (&replace_iterator))
{
action = lit_utf8_iterator_peek_next (&replace_iterator);
if (action == LIT_CHAR_DOLLAR_SIGN)
{
current_position++;
}
else if (action == LIT_CHAR_0)
{
lit_utf8_iterator_incr (&replace_iterator);
ecma_char_t next_character = lit_utf8_iterator_peek_next (&replace_iterator);
if (!(next_character >= LIT_CHAR_1 && next_character <= LIT_CHAR_9))
{
action = LIT_CHAR_NULL;
}
lit_utf8_iterator_decr (&replace_iterator);
}
else if (action != LIT_CHAR_AMPERSAND
&& action != LIT_CHAR_GRAVE_ACCENT
&& action != LIT_CHAR_SINGLE_QUOTE
&& !(action >= LIT_CHAR_1 && action <= LIT_CHAR_9))
{
action = LIT_CHAR_NULL;
}
}
}
if (action != LIT_CHAR_NULL)
{
result_string_p = ecma_builtin_string_prototype_object_replace_append_substr (result_string_p,
context_p->replace_string_p,
previous_start,
current_position,
true);
lit_utf8_iterator_incr (&replace_iterator);
if (action == LIT_CHAR_DOLLAR_SIGN)
{
current_position--;
}
else if (action == LIT_CHAR_GRAVE_ACCENT)
{
ecma_string_t *input_string_p = ecma_get_string_from_value (context_p->input_string);
result_string_p = ecma_builtin_string_prototype_object_replace_append_substr (result_string_p,
input_string_p,
0,
context_p->match_start,
true);
}
else if (action == LIT_CHAR_SINGLE_QUOTE)
{
ecma_string_t *input_string_p = ecma_get_string_from_value (context_p->input_string);
result_string_p = ecma_builtin_string_prototype_object_replace_append_substr (result_string_p,
input_string_p,
context_p->match_end,
context_p->input_length,
true);
}
else
{
/* Everything else is submatch reading. */
uint32_t index = 0;
JERRY_ASSERT (action == LIT_CHAR_AMPERSAND || (action >= LIT_CHAR_0 && action <= LIT_CHAR_9));
if (action >= LIT_CHAR_0 && action <= LIT_CHAR_9)
{
index = (uint32_t) (action - LIT_CHAR_0);
action = lit_utf8_iterator_peek_next (&replace_iterator);
if (action >= LIT_CHAR_0 && action <= LIT_CHAR_9)
{
index = index * 10 + (uint32_t) (action - LIT_CHAR_0);
lit_utf8_iterator_incr (&replace_iterator);
current_position++;
}
JERRY_ASSERT (index != 0);
}
ecma_string_t *index_string_p = ecma_new_ecma_string_from_uint32 (index);
ecma_object_t *match_object_p = ecma_get_object_from_value (match_value);
ECMA_TRY_CATCH (submatch_value,
ecma_op_object_get (match_object_p, index_string_p),
ret_value);
/* Undefined values are converted to empty string. */
if (!ecma_is_value_undefined (submatch_value))
{
JERRY_ASSERT (ecma_is_value_string (submatch_value));
ecma_string_t *submatch_string_p = ecma_get_string_from_value (submatch_value);
ecma_string_t *appended_string_p = ecma_concat_ecma_strings (result_string_p, submatch_string_p);
ecma_deref_ecma_string (result_string_p);
result_string_p = appended_string_p;
}
ECMA_FINALIZE (submatch_value);
ecma_deref_ecma_string (index_string_p);
if (!ecma_is_completion_value_empty (ret_value))
{
break;
}
}
current_position++;
previous_start = current_position + 1;
}
current_position++;
}
if (ecma_is_completion_value_empty (ret_value))
{
result_string_p = ecma_builtin_string_prototype_object_replace_append_substr (result_string_p,
context_p->replace_string_p,
previous_start,
current_position,
true);
ret_value = ecma_make_normal_completion_value (ecma_make_string_value (result_string_p));
}
}
return ret_value;
} /* ecma_builtin_string_prototype_object_replace_get_string */
/**
* Generic helper function to do the string replace
*
* @return completion value
* Returned value must be freed with ecma_free_completion_value.
*/
static ecma_completion_value_t
ecma_builtin_string_prototype_object_replace_loop (ecma_builtin_replace_search_ctx_t *context_p) /**< search
* context */
{
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
ecma_length_t previous_start = 0;
bool continue_match = true;
ecma_string_t *result_string_p = ecma_get_magic_string (LIT_MAGIC_STRING__EMPTY);
ecma_string_t *input_string_p = ecma_get_string_from_value (context_p->input_string);
while (continue_match)
{
continue_match = false;
ECMA_TRY_CATCH (match_value,
ecma_builtin_string_prototype_object_replace_match (context_p),
ret_value);
if (!ecma_is_value_null (match_value))
{
result_string_p = ecma_builtin_string_prototype_object_replace_append_substr (result_string_p,
input_string_p,
previous_start,
context_p->match_start,
true);
ECMA_TRY_CATCH (string_value,
ecma_builtin_string_prototype_object_replace_get_string (context_p, match_value),
ret_value);
JERRY_ASSERT (ecma_is_value_string (string_value));
ecma_string_t *appended_string_p = ecma_concat_ecma_strings (result_string_p,
ecma_get_string_from_value (string_value));
ecma_deref_ecma_string (result_string_p);
result_string_p = appended_string_p;
ECMA_FINALIZE (string_value);
previous_start = context_p->match_end;
if (context_p->is_global
&& ecma_is_completion_value_empty (ret_value)
&& context_p->match_start == context_p->match_end)
{
JERRY_ASSERT (context_p->is_regexp);
if (context_p->match_end == context_p->input_length)
{
/* Aborts the match. */
context_p->is_global = false;
}
else
{
ecma_string_t *last_index_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL);
ecma_object_t *regexp_obj_p = ecma_get_object_from_value (context_p->regexp_or_search_string);
ecma_number_t *zero_number_p = ecma_alloc_number ();
*zero_number_p = context_p->match_end + 1;
ECMA_TRY_CATCH (put_value,
ecma_op_object_put (regexp_obj_p,
last_index_string_p,
ecma_make_number_value (zero_number_p),
true),
ret_value);
ECMA_FINALIZE (put_value);
ecma_dealloc_number (zero_number_p);
ecma_deref_ecma_string (last_index_string_p);
}
}
}
if (ecma_is_completion_value_empty (ret_value))
{
if (!context_p->is_global || ecma_is_value_null (match_value))
{
/* No more matches */
ecma_string_t *appended_string_p;
appended_string_p = ecma_builtin_string_prototype_object_replace_append_substr (result_string_p,
input_string_p,
previous_start,
context_p->input_length,
false);
ret_value = ecma_make_normal_completion_value (ecma_make_string_value (appended_string_p));
}
else
{
continue_match = true;
}
}
ECMA_FINALIZE (match_value);
}
ecma_deref_ecma_string (result_string_p);
return ret_value;
} /* ecma_builtin_string_prototype_object_replace_loop */
/**
* Generic helper function to check whether the search value is callable.
* If it is not, the function converts the search value to string. The
* appropriate fields of the context were filled as well and the search
* loop is run afterwards.
*
* @return completion value
* Returned value must be freed with ecma_free_completion_value.
*/
static ecma_completion_value_t
ecma_builtin_string_prototype_object_replace_main (ecma_builtin_replace_search_ctx_t *context_p, /**< search
* context */
ecma_value_t replace_value) /**< replacement for a match */
{
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
if (ecma_op_is_callable (replace_value))
{
context_p->is_replace_callable = true;
context_p->replace_function_p = ecma_get_object_from_value (replace_value);
ret_value = ecma_builtin_string_prototype_object_replace_loop (context_p);
}
else
{
context_p->is_replace_callable = false;
ECMA_TRY_CATCH (to_string_replace_val,
ecma_op_to_string (replace_value),
ret_value);
ecma_string_t *replace_string_p = ecma_get_string_from_value (to_string_replace_val);
lit_utf8_size_t replace_size = ecma_string_get_size (replace_string_p);
MEM_DEFINE_LOCAL_ARRAY (replace_start_p,
replace_size,
lit_utf8_byte_t);
ecma_string_to_utf8_string (replace_string_p,
replace_start_p,
(ssize_t) (replace_size));
context_p->replace_string_p = replace_string_p;
context_p->replace_iterator = lit_utf8_iterator_create (replace_start_p, replace_size);
ret_value = ecma_builtin_string_prototype_object_replace_loop (context_p);
MEM_FINALIZE_LOCAL_ARRAY (replace_start_p);
ECMA_FINALIZE (to_string_replace_val);
}
return ret_value;
} /* ecma_builtin_string_prototype_object_replace_main */
/**
* The String.prototype object's 'replace' routine
*
* The replace algorithm is splitted into several helper functions.
* This allows using ECMA_TRY_CATCH macros and avoiding early returns.
*
* To share data between these helper functions, we created a
* structure called ecma_builtin_replace_search_ctx_t, which
* represents the current state of the replace.
*
* The helper functions are called in the following order:
*
* 1) ecma_builtin_string_prototype_object_replace is called
* it initialise the context depending on search_value (regexp or string)
* 2) ecma_builtin_string_prototype_object_replace_main is called
* it initialise the context depending on replace_value (callable or string)
* 3) ecma_builtin_string_prototype_object_replace_loop is called
* this function has a loop which repeatedly calls
* - ecma_builtin_string_prototype_object_replace_match
* which performs a match
* - ecma_builtin_string_prototype_object_replace_get_string
* which computes the replacement string
*
* The final string is created from several string fragments appended
* together by ecma_builtin_string_prototype_object_replace_append_substr.
*
* See also:
* ECMA-262 v5, 15.5.4.11
*
@ -756,11 +1421,90 @@ ecma_builtin_string_prototype_object_match (ecma_value_t this_arg, /**< this arg
*/
static ecma_completion_value_t
ecma_builtin_string_prototype_object_replace (ecma_value_t this_arg, /**< this argument */
ecma_value_t arg1, /**< routine's first argument */
ecma_value_t arg2) /**< routine's second argument */
ecma_value_t search_value, /**< routine's first argument */
ecma_value_t replace_value) /**< routine's second argument */
{
ECMA_BUILTIN_CP_UNIMPLEMENTED (this_arg, arg1, arg2);
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
/* 1. */
ECMA_TRY_CATCH (check_coercible_val,
ecma_op_check_object_coercible (this_arg),
ret_value);
/* 2. */
ECMA_TRY_CATCH (to_string_value,
ecma_op_to_string (this_arg),
ret_value);
ecma_builtin_replace_search_ctx_t context;
if (ecma_is_value_object (search_value)
&& ecma_object_get_class_name (ecma_get_object_from_value (search_value)) == LIT_MAGIC_STRING_REGEXP_UL)
{
ecma_object_t *regexp_obj_p = ecma_get_object_from_value (search_value);
ecma_string_t *global_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_GLOBAL);
ECMA_TRY_CATCH (global_value,
ecma_op_object_get (regexp_obj_p, global_string_p),
ret_value);
JERRY_ASSERT (ecma_is_value_boolean (global_value));
context.is_regexp = true;
context.is_global = ecma_is_value_true (global_value);
context.input_string = to_string_value;
context.input_length = ecma_string_get_length (ecma_get_string_from_value (to_string_value));
context.regexp_or_search_string = search_value;
if (context.is_global)
{
ecma_string_t *last_index_string_p = ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL);
ecma_number_t *zero_number_p = ecma_alloc_number ();
*zero_number_p = 0;
ECMA_TRY_CATCH (put_value,
ecma_op_object_put (regexp_obj_p,
last_index_string_p,
ecma_make_number_value (zero_number_p),
true),
ret_value);
ECMA_FINALIZE (put_value);
ecma_dealloc_number (zero_number_p);
ecma_deref_ecma_string (last_index_string_p);
}
if (ecma_is_completion_value_empty (ret_value))
{
ret_value = ecma_builtin_string_prototype_object_replace_main (&context, replace_value);
}
ECMA_FINALIZE (global_value);
ecma_deref_ecma_string (global_string_p);
}
else
{
ECMA_TRY_CATCH (to_string_search_val,
ecma_op_to_string (search_value),
ret_value);
context.is_regexp = false;
context.is_global = false;
context.input_string = to_string_value;
context.input_length = ecma_string_get_length (ecma_get_string_from_value (to_string_value));
context.regexp_or_search_string = to_string_search_val;
ret_value = ecma_builtin_string_prototype_object_replace_main (&context, replace_value);
ECMA_FINALIZE (to_string_search_val);
}
ECMA_FINALIZE (to_string_value);
ECMA_FINALIZE (check_coercible_val);
return ret_value;
} /* ecma_builtin_string_prototype_object_replace */
#endif /* !CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */
/**
* The String.prototype object's 'search' routine

View File

@ -70,9 +70,9 @@ ROUTINE (LIT_MAGIC_STRING_LOCALE_COMPARE_UL, ecma_builtin_string_prototype_objec
#ifndef CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN
ROUTINE (LIT_MAGIC_STRING_MATCH, ecma_builtin_string_prototype_object_match, 1, 1)
ROUTINE (LIT_MAGIC_STRING_REPLACE, ecma_builtin_string_prototype_object_replace, 2, 2)
#endif /* CONFIG_ECMA_COMPACT_PROFILE_DISABLE_REGEXP_BUILTIN */
ROUTINE (LIT_MAGIC_STRING_REPLACE, ecma_builtin_string_prototype_object_replace, 2, 2)
ROUTINE (LIT_MAGIC_STRING_SEARCH, ecma_builtin_string_prototype_object_search, 1, 1)
ROUTINE (LIT_MAGIC_STRING_SPLIT, ecma_builtin_string_prototype_object_split, 2, 2)
ROUTINE (LIT_MAGIC_STRING_SUBSTRING, ecma_builtin_string_prototype_object_substring, 2, 2)

View File

@ -108,6 +108,11 @@ extern bool lit_char_is_unicode_connector_punctuation (ecma_char_t);
#define LIT_CHAR_QUESTION ((ecma_char_t) '?') /* question mark */
#define LIT_CHAR_COLON ((ecma_char_t) ':') /* colon */
/*
* Special characters for String.prototype.replace.
*/
#define LIT_CHAR_GRAVE_ACCENT ((ecma_char_t) '`') /* grave accent */
/**
* Uppercase ASCII letters
*/

View File

@ -0,0 +1,91 @@
// Copyright 2015 University of Szeged
// Copyright 2015 Samsung Electronics Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
assert ("abcabc".replace("bc", ":") === "a:abc");
assert ("hello".replace("", ":") === ":hello");
assert ("xabcxabcx".replace (/abc/g, "[$&][$`][$']") === "x[abc][x][xabcx]x[abc][xabcx][x]x");
assert ("abc".replace (/a(b)c|d()/, "[$1][$01][$2][$02][$99][$123][$012]") === "[b][b][][][][3][b2]");
assert ("abc".replace("abc", "$x$$5$0$00$" === "$x$5$0$00$"));
assert ("a true true story".replace(true) === "a undefined true story");
assert ("1234".replace(23, 32) === "1324");
assert ("abcabc".replace(/bc/, ":") === "a:abc");
assert ("axbcxx".replace(/x*/g, ":") === ":a::b:c::");
assert (String.prototype.replace.call (12321, /2/g, ".") === "1.3.1");
try
{
String.prototype.replace.call (null, "u", ".");
assert (false);
}
catch (e)
{
assert (e instanceof TypeError);
}
assert ("98765".replace(76, function () { return {}; }) === "98[object Object]5");
function concat_arguments()
{
var str = "";
for (var i = 0; i < arguments.length; i++)
{
str += "[" + arguments[i] + "]";
}
return str;
}
assert ("abcdabcd".replace("cd", concat_arguments) === "ab[cd][2][abcdabcd]abcd");
assert ("abcdef".replace (/a((b)c)|d()/, concat_arguments) === "[abc][bc][b][undefined][0][abcdef]def");
try
{
"x".replace("x", function() { throw "MyError"; });
assert (false);
}
catch (e)
{
assert (e === "MyError");
}
assert ("\ud801\udc00".replace("\ud801", "#") === "#\udc00");
assert ("\ud801\udc00".replace("\udc00", "#") === "\ud801#");
var regexp = /r/g;
Object.defineProperty(regexp, "lastIndex", {
configurable : false,
enumerable : false,
value : 0,
writable : false
});
try {
"r".replace (regexp, "x");
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
// The real "exec" never returns with a number.
Object.getPrototypeOf(/x/).exec = function () { return 1234; }
assert (/y/.exec("y") === 1234);
// Changing exec should not affect replace.
assert ("y".replace (/y/, "x") === "x");