Implement and add support for Symbol.split (#3453)

JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai@inf.u-szeged.hu
This commit is contained in:
Dániel Bátyai
2019-12-20 09:10:58 +01:00
committed by Robert Fancsik
parent 85bb2113fe
commit 2a29b72a83
10 changed files with 841 additions and 318 deletions
@@ -633,6 +633,23 @@ ecma_builtin_regexp_prototype_symbol_search (ecma_value_t this_arg, /**< this ar
return ecma_regexp_search_helper (this_arg, string_arg);
} /* ecma_builtin_regexp_prototype_symbol_search */
/**
* The RegExp.prototype object's '@@split' routine
*
* See also:
* ECMA-262 v6.0, 21.2.5.11
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_regexp_prototype_symbol_split (ecma_value_t this_arg, /**< this argument */
ecma_value_t string_arg, /**< source string */
ecma_value_t limit_arg) /**< limit */
{
return ecma_regexp_split_helper (this_arg, string_arg, limit_arg);
} /* ecma_builtin_regexp_prototype_symbol_split */
/**
* The RegExp.prototype object's '@@match' routine
*
@@ -57,6 +57,7 @@ ACCESSOR_READ_ONLY (LIT_MAGIC_STRING_STICKY,
ROUTINE (LIT_GLOBAL_SYMBOL_REPLACE, ecma_builtin_regexp_prototype_symbol_replace, 2, 2)
ROUTINE (LIT_GLOBAL_SYMBOL_SEARCH, ecma_builtin_regexp_prototype_symbol_search, 1, 1)
ROUTINE (LIT_GLOBAL_SYMBOL_SPLIT, ecma_builtin_regexp_prototype_symbol_split, 2, 2)
ROUTINE (LIT_GLOBAL_SYMBOL_MATCH, ecma_builtin_regexp_prototype_symbol_match, 1, 1)
#else /* !ENABLED (JERRY_ES2015) */
/* ECMA-262 v5, 15.10.7.1 */
@@ -726,337 +726,183 @@ ecma_builtin_string_prototype_object_slice (ecma_string_t *get_string_val, /**<
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_string_prototype_object_split (ecma_value_t this_to_string_val, /**< this argument */
ecma_value_t arg1, /**< separator */
ecma_value_t arg2) /**< limit */
ecma_builtin_string_prototype_object_split (ecma_value_t this_value, /**< this argument */
ecma_value_t separator_value, /**< separator */
ecma_value_t limit_value) /**< limit */
{
/* 5. */
ecma_length_t limit = 0;
if (ecma_is_value_undefined (arg2))
#if ENABLED (JERRY_ES2015)
if (!(ecma_is_value_undefined (separator_value) || ecma_is_value_null (separator_value)))
{
limit = (uint32_t) -1;
}
else
{
ecma_number_t limit_num;
ecma_object_t *obj_p = ecma_get_object_from_value (ecma_op_to_object (separator_value));
ecma_value_t split_symbol = ecma_op_object_get_by_symbol_id (obj_p, LIT_MAGIC_STRING_SPLIT);
ecma_deref_object (obj_p);
if (ECMA_IS_VALUE_ERROR (ecma_get_number (arg2, &limit_num)))
if (ECMA_IS_VALUE_ERROR (split_symbol))
{
return ECMA_VALUE_ERROR;
return split_symbol;
}
limit = ecma_number_to_uint32 (limit_num);
if (!ecma_is_value_undefined (split_symbol) && !ecma_is_value_null (split_symbol))
{
if (!ecma_op_is_callable (split_symbol))
{
ecma_free_value (split_symbol);
return ecma_raise_type_error (ECMA_ERR_MSG ("@@split is not callable"));
}
ecma_object_t *split_method_p = ecma_get_object_from_value (split_symbol);
ecma_value_t arguments[] = { this_value, limit_value };
ecma_value_t split_result = ecma_op_function_call (split_method_p, separator_value, arguments, 2);
ecma_deref_object (split_method_p);
return split_result;
}
}
/* 3. */
ecma_value_t new_array = ecma_op_create_array_object (0, 0, false);
if (limit == 0)
#else /* !ENABLED (JERRY_ES2015) */
if (ecma_object_is_regexp_object (separator_value))
{
return new_array;
return ecma_regexp_split_helper (separator_value, this_value, limit_value);
}
#endif /* ENABLED (JERRY_ES2015) */
ecma_object_t *new_array_p = ecma_get_object_from_value (new_array);
ecma_value_t result = ECMA_VALUE_ERROR;
/* 10. */
if (ecma_is_value_undefined (arg1))
/* 4. */
ecma_string_t *string_p = ecma_op_to_string (this_value);
if (string_p == NULL)
{
ecma_value_t put_comp = ecma_builtin_helper_def_prop_by_index (new_array_p,
0,
this_to_string_val,
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
JERRY_ASSERT (ecma_is_value_true (put_comp));
return new_array;
return result;
}
/* 8. */
ecma_value_t separator = ECMA_VALUE_EMPTY;
bool separator_is_regexp = false;
if (ecma_object_is_regexp_object (arg1))
uint32_t limit = UINT32_MAX;
if (!ecma_is_value_undefined (limit_value))
{
separator_is_regexp = true;
separator = ecma_copy_value (arg1);
}
else
{
ecma_string_t *separator_to_string_p = ecma_op_to_string (arg1);
if (JERRY_UNLIKELY (separator_to_string_p == NULL))
if (ECMA_IS_VALUE_ERROR (ecma_op_to_length (limit_value, &limit)))
{
ecma_deref_object (new_array_p);
return ECMA_VALUE_ERROR;
goto cleanup_string;
}
separator = ecma_make_string_value (separator_to_string_p);
}
const ecma_string_t *this_to_string_p = ecma_get_string_from_value (this_to_string_val);
/* 11. */
if (ecma_string_is_empty (this_to_string_p))
/* 12. */
ecma_string_t *separator_p = ecma_op_to_string (separator_value);
if (separator_p == NULL)
{
bool should_return = false;
goto cleanup_string;
}
if (separator_is_regexp)
{
#if ENABLED (JERRY_BUILTIN_REGEXP)
ecma_value_t regexp_value = ecma_copy_value_if_not_object (separator);
ecma_value_t match_result;
match_result = ecma_regexp_exec_helper (regexp_value,
ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY),
true);
should_return = !ecma_is_value_null (match_result);
/* 6. */
result = ecma_op_create_array_object (NULL, 0, false);
if (ECMA_IS_VALUE_ERROR (match_result))
{
match_result = jcontext_take_exception ();
}
/* 14. */
if (limit == 0)
{
goto cleanup_separator;
}
ecma_free_value (match_result);
#else /* !ENABLED (JERRY_BUILTIN_REGEXP) */
return ecma_raise_type_error (ECMA_ERR_MSG ("REGEXP separator is disabled in split method."));
#endif /* ENABLED (JERRY_BUILTIN_REGEXP) */
}
else
{
ecma_string_t *separator_str_p = ecma_get_string_from_value (separator);
ecma_object_t *array_p = ecma_get_object_from_value (result);
ecma_length_t array_length = 0;
if (ecma_string_get_length (separator_str_p) == 0)
{
should_return = true;
}
}
if (!should_return)
{
/* 11.c */
ecma_value_t put_comp = ecma_builtin_helper_def_prop_by_index (new_array_p,
0,
this_to_string_val,
/* 15. */
if (ecma_is_value_undefined (separator_value))
{
ecma_value_t put_result = ecma_builtin_helper_def_prop_by_index (array_p,
array_length,
ecma_make_string_value (string_p),
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
JERRY_ASSERT (ecma_is_value_true (put_comp));
}
JERRY_ASSERT (put_result == ECMA_VALUE_TRUE);
goto cleanup_separator;
}
else
/* 16. */
if (ecma_string_is_empty (string_p))
{
/* 4. */
ecma_length_t new_array_length = 0;
/* 7. */
ecma_length_t start_pos = 0;
/* 12. */
ecma_length_t curr_pos = start_pos;
bool separator_is_empty = false;
bool should_return = false;
/* 6. */
const ecma_length_t string_length = ecma_string_get_length (this_to_string_p);
while (curr_pos < string_length && !should_return)
if (!ecma_string_is_empty (separator_p))
{
ecma_value_t match_result = ECMA_VALUE_NULL;
if (separator_is_regexp)
{
#if ENABLED (JERRY_BUILTIN_REGEXP)
ecma_value_t regexp_value = ecma_copy_value_if_not_object (separator);
ecma_string_t *substr_str_p = ecma_string_substr (this_to_string_p, curr_pos, string_length);
match_result = ecma_regexp_exec_helper (regexp_value, ecma_make_string_value (substr_str_p), true);
ecma_deref_ecma_string (substr_str_p);
#else /* !ENABLED (JERRY_BUILTIN_REGEXP) */
return ecma_raise_type_error (ECMA_ERR_MSG ("REGEXP separator is disabled in split method."));
#endif /* ENABLED (JERRY_BUILTIN_REGEXP) */
}
else
{
ecma_string_t *separator_str_p = ecma_get_string_from_value (separator);
ecma_length_t separator_length = ecma_string_get_length (separator_str_p);
if (curr_pos + separator_length <= string_length)
{
bool is_different = false;
for (ecma_length_t i = 0; i < separator_length && !is_different; i++)
{
ecma_char_t char_from_string = ecma_string_get_char_at_pos (this_to_string_p, curr_pos + i);
ecma_char_t char_from_separator = ecma_string_get_char_at_pos (separator_str_p, i);
is_different = (char_from_string != char_from_separator);
}
if (!is_different)
{
/* 6-7. */
match_result = ecma_op_create_array_object (0, 0, false);
}
}
}
if (ecma_is_value_null (match_result) || ECMA_IS_VALUE_ERROR (match_result))
{
curr_pos++;
if (ECMA_IS_VALUE_ERROR (match_result))
{
jcontext_release_exception ();
}
}
else
{
ecma_object_t *match_obj_p = ecma_get_object_from_value (match_result);
ecma_string_t *zero_str_p = ecma_get_ecma_string_from_uint32 (0);
ecma_string_t *magic_index_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_INDEX);
ecma_value_t index_prop_value;
if (separator_is_regexp)
{
JERRY_ASSERT (!ecma_op_object_is_fast_array (match_obj_p));
ecma_property_value_t *index_prop_value_p = ecma_get_named_data_property (match_obj_p, magic_index_str_p);
ecma_number_t index_num = ecma_get_number_from_value (index_prop_value_p->value);
ecma_value_assign_number (&index_prop_value_p->value, index_num + (ecma_number_t) curr_pos);
index_prop_value = index_prop_value_p->value;
}
else
{
const uint32_t opts = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE | ECMA_IS_THROW;
ecma_string_t *separator_str_p = ecma_get_string_from_value (separator);
ecma_value_t put_comp = ecma_builtin_helper_def_prop (match_obj_p,
zero_str_p,
ecma_make_string_value (separator_str_p),
opts);
JERRY_ASSERT (ecma_is_value_true (put_comp));
index_prop_value = ecma_make_uint32_value (curr_pos);
put_comp = ecma_builtin_helper_def_prop (match_obj_p,
magic_index_str_p,
index_prop_value,
ECMA_PROPERTY_FLAG_WRITABLE);
JERRY_ASSERT (ecma_is_value_true (put_comp));
}
ecma_value_t match_comp_value = ecma_op_object_get (match_obj_p, zero_str_p);
JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (match_comp_value));
ecma_string_t *match_str_p = ecma_get_string_from_value (match_comp_value);
ecma_length_t match_str_length = ecma_string_get_length (match_str_p);
separator_is_empty = ecma_string_is_empty (match_str_p);
ecma_free_value (match_comp_value);
ecma_number_t index_num = ecma_get_number_from_value (index_prop_value);
JERRY_ASSERT (index_num >= 0);
uint32_t end_pos = ecma_number_to_uint32 (index_num);
if (separator_is_empty)
{
end_pos = curr_pos + 1;
}
/* 13.c.iii.1-2 */
ecma_string_t *substr_str_p = ecma_string_substr (this_to_string_p,
start_pos,
end_pos);
ecma_value_t put_comp;
put_comp = ecma_builtin_helper_def_prop_by_index (new_array_p,
new_array_length,
ecma_make_string_value (substr_str_p),
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
JERRY_ASSERT (ecma_is_value_true (put_comp));
/* 13.c.iii.3 */
new_array_length++;
/* 13.c.iii.4 */
if (new_array_length == limit)
{
should_return = true;
}
/* 13.c.iii.5 */
start_pos = end_pos + match_str_length;
const uint32_t match_result_array_length = ecma_array_get_length (match_obj_p) - 1;
/* 13.c.iii.6 */
uint32_t i = 0;
/* 13.c.iii.7 */
while (i < match_result_array_length)
{
/* 13.c.iii.7.a */
i++;
match_comp_value = ecma_op_object_get_by_uint32_index (match_obj_p, i);
JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (match_comp_value));
/* 13.c.iii.7.b */
put_comp = ecma_builtin_helper_def_prop_by_index (new_array_p,
new_array_length,
match_comp_value,
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
JERRY_ASSERT (ecma_is_value_true (put_comp));
/* 13.c.iii.7.c */
new_array_length++;
/* 13.c.iii.7.d */
if (new_array_length == limit)
{
should_return = true;
}
ecma_free_value (match_comp_value);
}
/* 13.c.iii.8 */
curr_pos = start_pos;
ecma_deref_ecma_string (substr_str_p);
}
ecma_free_value (match_result);
ecma_value_t put_result = ecma_builtin_helper_def_prop_by_index (array_p,
array_length,
ecma_make_string_value (string_p),
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
JERRY_ASSERT (put_result == ECMA_VALUE_TRUE);
}
if (!should_return && !separator_is_empty)
{
/* 14. */
ecma_string_t *substr_str_p;
substr_str_p = ecma_string_substr (this_to_string_p,
start_pos,
string_length);
/* 15. */
ecma_value_t put_comp;
put_comp = ecma_builtin_helper_def_prop_by_index (new_array_p,
new_array_length,
ecma_make_string_value (substr_str_p),
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
JERRY_ASSERT (ecma_is_value_true (put_comp));
ecma_deref_ecma_string (substr_str_p);
}
goto cleanup_separator;
}
ecma_free_value (separator);
lit_utf8_size_t string_size;
uint8_t string_flags = ECMA_STRING_FLAG_IS_ASCII;
const lit_utf8_byte_t *string_buffer_p = ecma_string_get_chars (string_p,
&string_size,
NULL,
NULL,
&string_flags);
lit_utf8_size_t separator_size;
uint8_t separator_flags = ECMA_STRING_FLAG_IS_ASCII;
const lit_utf8_byte_t *separator_buffer_p = ecma_string_get_chars (separator_p,
&separator_size,
NULL,
NULL,
&separator_flags);
return new_array;
const lit_utf8_byte_t *const string_end_p = string_buffer_p + string_size;
const lit_utf8_byte_t *const compare_end_p = JERRY_MIN (string_end_p - separator_size + 1,
string_end_p);
const lit_utf8_byte_t *current_p = string_buffer_p;
const lit_utf8_byte_t *last_str_begin_p = string_buffer_p;
while (current_p < compare_end_p)
{
if (!memcmp (current_p, separator_buffer_p, separator_size)
&& (last_str_begin_p != current_p + separator_size))
{
ecma_string_t *substr_p = ecma_new_ecma_string_from_utf8 (last_str_begin_p,
(lit_utf8_size_t) (current_p - last_str_begin_p));
ecma_value_t put_result = ecma_builtin_helper_def_prop_by_index (array_p,
array_length++,
ecma_make_string_value (substr_p),
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
JERRY_ASSERT (put_result == ECMA_VALUE_TRUE);
ecma_deref_ecma_string (substr_p);
if (array_length >= limit)
{
goto cleanup_buffers;
}
current_p += separator_size;
last_str_begin_p = current_p;
continue;
}
lit_utf8_incr (&current_p);
}
ecma_string_t *end_substr_p = ecma_new_ecma_string_from_utf8 (last_str_begin_p,
(lit_utf8_size_t) (string_end_p - last_str_begin_p));
ecma_value_t put_result = ecma_builtin_helper_def_prop_by_index (array_p,
array_length,
ecma_make_string_value (end_substr_p),
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
JERRY_ASSERT (put_result == ECMA_VALUE_TRUE);
ecma_deref_ecma_string (end_substr_p);
cleanup_buffers:
if (string_flags & ECMA_STRING_FLAG_MUST_BE_FREED)
{
jmem_heap_free_block ((void *) string_buffer_p, string_size);
}
if (separator_flags & ECMA_STRING_FLAG_MUST_BE_FREED)
{
jmem_heap_free_block ((void *) separator_buffer_p, separator_size);
}
cleanup_separator:
ecma_deref_ecma_string (separator_p);
cleanup_string:
ecma_deref_ecma_string (string_p);
return result;
} /* ecma_builtin_string_prototype_object_split */
/**