diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 13df9a30e..4a275c432 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -181,6 +181,7 @@ Well-known symbols: - JERRY_SYMBOL_TO_PRIMITIVE - @@toPrimitive well-known symbol - JERRY_SYMBOL_TO_STRING_TAG - @@toStringTag well-known symbol - JERRY_SYMBOL_UNSCOPABLES - @@unscopables well-known symbol + - JERRY_SYMBOL_MATCH_ALL - @@matchAll well-known symbol *New in version 2.4*. diff --git a/jerry-core/CMakeLists.txt b/jerry-core/CMakeLists.txt index fbbeb161c..46a2b89a1 100644 --- a/jerry-core/CMakeLists.txt +++ b/jerry-core/CMakeLists.txt @@ -199,6 +199,7 @@ set(SOURCE_CORE_FILES ecma/builtin-objects/ecma-builtin-referenceerror.c ecma/builtin-objects/ecma-builtin-reflect.c ecma/builtin-objects/ecma-builtin-regexp-prototype.c + ecma/builtin-objects/ecma-builtin-regexp-string-iterator-prototype.c ecma/builtin-objects/ecma-builtin-regexp.c ecma/builtin-objects/ecma-builtin-set-iterator-prototype.c ecma/builtin-objects/ecma-builtin-set-prototype.c @@ -386,6 +387,7 @@ if(ENABLE_AMALGAM) ecma/builtin-objects/ecma-builtin-referenceerror.inc.h ecma/builtin-objects/ecma-builtin-reflect.inc.h ecma/builtin-objects/ecma-builtin-regexp-prototype.inc.h + ecma/builtin-objects/ecma-builtin-regexp-string-iterator-prototype.inc.h ecma/builtin-objects/ecma-builtin-regexp.inc.h ecma/builtin-objects/ecma-builtin-set-iterator-prototype.inc.h ecma/builtin-objects/ecma-builtin-set-prototype.inc.h diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 44a6500bd..e32f338d7 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -813,6 +813,13 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ { break; } + case ECMA_PSEUDO_REGEXP_STRING_ITERATOR: + { + ecma_regexp_string_iterator_t *regexp_string_iterator_obj = (ecma_regexp_string_iterator_t *) object_p; + ecma_value_t regexp = regexp_string_iterator_obj->iterating_regexp; + ecma_gc_set_object_visited (ecma_get_object_from_value (regexp)); + break; + } #endif /* ENABLED (JERRY_ESNEXT) */ default: { @@ -1728,6 +1735,19 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ break; } + case ECMA_PSEUDO_REGEXP_STRING_ITERATOR: + { + ecma_regexp_string_iterator_t *regexp_string_iterator_obj = (ecma_regexp_string_iterator_t *) object_p; + ecma_value_t iterated_string = regexp_string_iterator_obj->iterated_string; + + if (!ecma_is_value_empty (iterated_string)) + { + ecma_deref_ecma_string (ecma_get_string_from_value (iterated_string)); + } + + ext_object_size = sizeof (ecma_regexp_string_iterator_t); + break; + } #endif /* ENABLED (JERRY_ESNEXT) */ default: { diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 680a738d4..3af2d78c7 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -685,6 +685,7 @@ typedef enum ECMA_PSEUDO_SET_ITERATOR = 4, /**< Set iterator object (ECMAScript v6, 23.2.5.1) */ ECMA_PSEUDO_MAP_ITERATOR = 5, /**< Map iterator object (ECMAScript v6, 23.1.5.1) */ ECMA_PSEUDO_STRING_ITERATOR = 6, /**< String iterator object (ECMAScript v6, 22.1.5.1) */ + ECMA_PSEUDO_REGEXP_STRING_ITERATOR = 7, /** RegExp string iterator object (ECMAScript v11, 21.2.7) */ ECMA_PSEUDO_ARRAY__MAX = ECMA_PSEUDO_STRING_ITERATOR /**< maximum value */ } ecma_pseudo_array_type_t; diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.c index 99bbf3e75..ca4681daa 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.c @@ -68,6 +68,7 @@ enum ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_REPLACE, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_SPLIT, + ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH_ALL, #endif /* ENABLED (JERRY_ESNEXT) */ }; @@ -514,6 +515,147 @@ ecma_builtin_is_regexp_exec (ecma_extended_object_t *obj_p) } /* ecma_builtin_is_regexp_exec */ #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_ESNEXT) +/** + * The RegExp.prototype object's 'matchAll' routine + * + * See also: + * ECMA-262 v11, 21.2.5.8 + * + * @return ecma_value_t + */ +static ecma_value_t +ecma_builtin_regexp_prototype_match_all (ecma_object_t *regexp_obj_p, /**< this argument */ + ecma_value_t string_arg) /**< source string */ +{ + /* 3. */ + ecma_string_t *str_p = ecma_op_to_string (string_arg); + + if (JERRY_UNLIKELY (str_p == NULL)) + { + return ECMA_VALUE_ERROR; + } + + /* 4. */ + ecma_value_t constructor = ecma_op_species_constructor (regexp_obj_p, ECMA_BUILTIN_ID_REGEXP); + + if (ECMA_IS_VALUE_ERROR (constructor)) + { + ecma_deref_ecma_string (str_p); + return constructor; + } + + /* 5. */ + ecma_value_t get_flag = ecma_op_object_get_by_magic_id (regexp_obj_p, LIT_MAGIC_STRING_FLAGS); + + if (ECMA_IS_VALUE_ERROR (get_flag)) + { + ecma_deref_ecma_string (str_p); + ecma_free_value (constructor); + return get_flag; + } + + ecma_string_t *flags = ecma_op_to_string (get_flag); + + ecma_free_value (get_flag); + + if (JERRY_UNLIKELY (flags == NULL)) + { + ecma_deref_ecma_string (str_p); + ecma_free_value (constructor); + return ECMA_VALUE_ERROR; + } + + /* 6. */ + ecma_object_t *constructor_obj_p = ecma_get_object_from_value (constructor); + ecma_value_t flags_value = ecma_make_string_value (flags); + ecma_value_t match_args[] = { ecma_make_object_value (regexp_obj_p), flags_value}; + ecma_value_t matcher = ecma_op_function_construct (constructor_obj_p, constructor_obj_p, match_args, 2); + + ecma_deref_object (constructor_obj_p); + + if (ECMA_IS_VALUE_ERROR (matcher)) + { + ecma_deref_ecma_string (str_p); + ecma_deref_ecma_string (flags); + return matcher; + } + + /* 7. */ + ecma_value_t get_last_index = ecma_op_object_get_by_magic_id (regexp_obj_p, LIT_MAGIC_STRING_LASTINDEX_UL); + + if (ECMA_IS_VALUE_ERROR (get_last_index)) + { + ecma_deref_ecma_string (str_p); + ecma_deref_ecma_string (flags); + ecma_free_value (matcher); + return get_last_index; + } + + ecma_length_t last_index; + ecma_value_t to_len = ecma_op_to_length (get_last_index, &last_index); + + ecma_free_value (get_last_index); + + if (ECMA_IS_VALUE_ERROR (to_len)) + { + ecma_deref_ecma_string (str_p); + ecma_deref_ecma_string (flags); + ecma_free_value (matcher); + return to_len; + } + + /* 8. */ + ecma_object_t *matcher_obj_p = ecma_get_object_from_value (matcher); + ecma_value_t last_index_value = ecma_make_length_value (last_index); + ecma_value_t set = ecma_op_object_put (matcher_obj_p, + ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), + last_index_value, + true); + + ecma_free_value (last_index_value); + + if (ECMA_IS_VALUE_ERROR (set)) + { + ecma_deref_ecma_string (str_p); + ecma_deref_ecma_string (flags); + ecma_deref_object (matcher_obj_p); + } + + uint16_t parsed_flag; + ecma_value_t flag_parse = ecma_regexp_parse_flags (flags, &parsed_flag); + + ecma_deref_ecma_string (flags); + + if (ECMA_IS_VALUE_ERROR (flag_parse)) + { + ecma_deref_ecma_string (str_p); + ecma_deref_object (matcher_obj_p); + return flag_parse; + } + + /* 13. */ + ecma_object_t *result_obj; + ecma_object_t *proto_p = ecma_builtin_get (ECMA_BUILTIN_ID_REGEXP_STRING_ITERATOR_PROTOTYPE); + result_obj = ecma_create_object (proto_p, + sizeof (ecma_regexp_string_iterator_t), + ECMA_OBJECT_TYPE_PSEUDO_ARRAY); + + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) result_obj; + ext_obj_p->u.pseudo_array.type = (uint8_t) ECMA_PSEUDO_REGEXP_STRING_ITERATOR; + ext_obj_p->u.pseudo_array.extra_info = (uint8_t) (parsed_flag & (RE_FLAG_GLOBAL | RE_FLAG_UNICODE)); + + ecma_regexp_string_iterator_t *regexp_string_iterator_obj = (ecma_regexp_string_iterator_t *) result_obj; + + regexp_string_iterator_obj->iterating_regexp = matcher; + regexp_string_iterator_obj->iterated_string = ecma_make_string_value (str_p); + + ecma_deref_object (matcher_obj_p); + + return ecma_make_object_value (result_obj); +} /* ecma_builtin_regexp_prototype_match_all */ +#endif /* ENABLED (JERRY_ESNEXT) */ + /** * Dispatcher of the Regexp built-in's routines * @@ -584,6 +726,10 @@ ecma_builtin_regexp_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< { return ecma_regexp_match_helper (this_arg, arguments_list_p[0]); } + case ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH_ALL: + { + return ecma_builtin_regexp_prototype_match_all (obj_p, arguments_list_p[0]); + } case ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_REPLACE: { return ecma_regexp_replace_helper (this_arg, arguments_list_p[0], arguments_list_p[1]); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.inc.h index 6809ac8eb..7995aae8c 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.inc.h @@ -63,6 +63,7 @@ ROUTINE (LIT_GLOBAL_SYMBOL_REPLACE, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_REPLACE ROUTINE (LIT_GLOBAL_SYMBOL_SEARCH, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_SEARCH, 1, 1) ROUTINE (LIT_GLOBAL_SYMBOL_SPLIT, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_SPLIT, 2, 2) ROUTINE (LIT_GLOBAL_SYMBOL_MATCH, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH, 1, 1) +ROUTINE (LIT_GLOBAL_SYMBOL_MATCH_ALL, ECMA_REGEXP_PROTOTYPE_ROUTINE_SYMBOL_MATCH_ALL, 1, 1) #else /* !ENABLED (JERRY_ESNEXT) */ /* ECMA-262 v5, 15.10.7.1 */ STRING_VALUE (LIT_MAGIC_STRING_SOURCE, diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-string-iterator-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-string-iterator-prototype.c new file mode 100644 index 000000000..9839bc3a8 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-string-iterator-prototype.c @@ -0,0 +1,192 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * 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. + */ + +#include "ecma-builtin-helpers.h" +#include "ecma-builtins.h" +#include "ecma-conversion.h" +#include "ecma-gc.h" +#include "ecma-iterator-object.h" +#include "ecma-objects.h" + +#if ENABLED (JERRY_ESNEXT) + +#define ECMA_BUILTINS_INTERNAL +#include "ecma-builtins-internal.h" + +#define BUILTIN_INC_HEADER_NAME "ecma-builtin-regexp-string-iterator-prototype.inc.h" +#define BUILTIN_UNDERSCORED_ID regexp_string_iterator_prototype +#include "ecma-builtin-internal-routines-template.inc.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabuiltins + * @{ + * + * \addtogroup %regexpstringiteratorprototype% ECMA %ArrayIteratorPrototype% object built-in + * @{ + */ + +/** + * The %RegExpStringIteratorPrototype% object's 'next' routine + * + * See also: + * ECMA-262 v11, 21.2.7.1.1 + * + * Note: + * Returned value must be freed with ecma_free_value. + * + * @return iterator result object, if success + * error - otherwise + */ +static ecma_value_t +ecma_builtin_regexp_string_iterator_prototype_object_next (ecma_value_t this_val) /**< this argument */ +{ + /* 2. */ + if (!ecma_is_value_object (this_val)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not an object.")); + } + + ecma_object_t *obj_p = ecma_get_object_from_value (this_val); + ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; + + /* 3. */ + if (ecma_get_object_type (obj_p) != ECMA_OBJECT_TYPE_PSEUDO_ARRAY + || ext_obj_p->u.pseudo_array.type != ECMA_PSEUDO_REGEXP_STRING_ITERATOR) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not an iterator.")); + } + + ecma_regexp_string_iterator_t *regexp_string_iterator_obj = (ecma_regexp_string_iterator_t *) obj_p; + + /* 4. */ + if (ecma_is_value_empty (regexp_string_iterator_obj->iterated_string)) + { + return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); + } + + /* 5. */ + ecma_value_t regexp = regexp_string_iterator_obj->iterating_regexp; + + /* 6. */ + ecma_value_t matcher_str_value = regexp_string_iterator_obj->iterated_string; + ecma_string_t *matcher_str_p = ecma_get_string_from_value (matcher_str_value); + + /* 9. */ + ecma_value_t match = ecma_op_regexp_exec (regexp, matcher_str_p); + + if (ECMA_IS_VALUE_ERROR (match)) + { + return match; + } + + /* 10. */ + if (ecma_is_value_null (match)) + { + ecma_free_value (regexp_string_iterator_obj->iterated_string); + regexp_string_iterator_obj->iterated_string = ECMA_VALUE_EMPTY; + return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); + } + + ecma_object_t *match_result_array_p = ecma_get_object_from_value (match); + + ecma_value_t result = ECMA_VALUE_ERROR; + + /* 11. */ + if (regexp_string_iterator_obj->header.u.pseudo_array.extra_info & RE_FLAG_GLOBAL) + { + ecma_value_t matched_str_value = ecma_op_object_get_by_index (match_result_array_p, 0); + + if (ECMA_IS_VALUE_ERROR (matched_str_value)) + { + goto free_variables; + } + + ecma_string_t *matched_str_p = ecma_op_to_string (matched_str_value); + + ecma_free_value (matched_str_value); + + if (JERRY_UNLIKELY (matched_str_p == NULL)) + { + ecma_deref_ecma_string (matched_str_p); + goto free_variables; + } + + if (ecma_string_is_empty (matched_str_p)) + { + ecma_object_t *regexp_obj_p = ecma_get_object_from_value (regexp); + + ecma_value_t get_last_index = ecma_op_object_get_by_magic_id (regexp_obj_p, LIT_MAGIC_STRING_LASTINDEX_UL); + + if (ECMA_IS_VALUE_ERROR (get_last_index)) + { + goto free_variables; + } + + ecma_length_t this_index; + ecma_value_t to_len = ecma_op_to_length (get_last_index, &this_index); + + ecma_free_value (get_last_index); + + if (ECMA_IS_VALUE_ERROR (to_len)) + { + goto free_variables; + } + + bool full_unciode = (regexp_string_iterator_obj->header.u.pseudo_array.extra_info & RE_FLAG_UNICODE) != 0; + ecma_length_t next_index = ecma_op_advance_string_index (matcher_str_p, + this_index, + full_unciode); + + ecma_value_t next_index_value = ecma_make_length_value (next_index); + ecma_value_t set = ecma_op_object_put (regexp_obj_p, + ecma_get_magic_string (LIT_MAGIC_STRING_LASTINDEX_UL), + next_index_value, + true); + + ecma_free_value (next_index_value); + + if (ECMA_IS_VALUE_ERROR (set)) + { + goto free_variables; + } + } + else + { + ecma_deref_ecma_string (matched_str_p); + } + } + else + { + ecma_free_value (regexp_string_iterator_obj->iterated_string); + regexp_string_iterator_obj->iterated_string = ECMA_VALUE_EMPTY; + } + + result = ecma_create_iter_result_object (match, ECMA_VALUE_FALSE); + +free_variables: + ecma_deref_object (match_result_array_p); + + return result; +} /* ecma_builtin_regexp_string_iterator_prototype_object_next */ + +/** + * @} + * @} + * @} + */ + +#endif /* ENABLED (JERRY_ESNEXT) */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-string-iterator-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-string-iterator-prototype.inc.h new file mode 100644 index 000000000..ad7b2b6ad --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-string-iterator-prototype.inc.h @@ -0,0 +1,34 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * 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. + */ + +/* + * %RegExpStringIteratorPrototype% built-in description + */ + +#include "ecma-builtin-helpers-macro-defines.inc.h" + +#if ENABLED (JERRY_ESNEXT) + +STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, + LIT_MAGIC_STRING_REGEXP_STRING_ITERATOR_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +/* Routine properties: + * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ +ROUTINE (LIT_MAGIC_STRING_NEXT, ecma_builtin_regexp_string_iterator_prototype_object_next, 0, 0) + +#endif /* ENABLED (JERRY_ESNEXT) */ + +#include "ecma-builtin-helpers-macro-undefs.inc.h" 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 505d41ec6..04d591771 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.c @@ -91,6 +91,7 @@ enum ECMA_STRING_PROTOTYPE_ITERATOR, ECMA_STRING_PROTOTYPE_REPLACE_ALL, + ECMA_STRING_PROTOTYPE_MATCH_ALL, }; #define BUILTIN_INC_HEADER_NAME "ecma-builtin-string-prototype.inc.h" @@ -382,37 +383,128 @@ ecma_builtin_string_prototype_object_match (ecma_value_t this_argument, /**< thi #if ENABLED (JERRY_ESNEXT) /** - * Helper method to find a specific character in a string + * The String.prototype object's 'matchAll' routine * - * Used by: - * ecma_builtin_string_prototype_object_replace_helper + * See also: + * ECMA-262 v11, 21.1.3.12 * - * @return true - if the given character is in the string - * false - otherwise + * @return ecma value + * Returned value must be freed with ecma_free_value. */ -static bool -ecma_find_char_in_string (ecma_string_t *str_p, /**< source string */ - lit_utf8_byte_t c) /**< character to find*/ +static ecma_value_t +ecma_builtin_string_prototype_object_match_all (ecma_value_t this_argument, /**< this argument */ + ecma_value_t regexp_arg) /**< routine's argument */ { - ECMA_STRING_TO_UTF8_STRING (str_p, start_p, start_size); - - const lit_utf8_byte_t *str_curr_p = start_p; - const lit_utf8_byte_t *str_end_p = start_p + start_size; - bool have_char = false; - - while (str_curr_p < str_end_p) + /* 2. */ + if (!ecma_is_value_null (regexp_arg) && !ecma_is_value_undefined (regexp_arg)) { - if (*str_curr_p++ == c) + /* 2.a */ + ecma_value_t is_regexp = ecma_op_is_regexp (regexp_arg); + + if (ECMA_IS_VALUE_ERROR (is_regexp)) { - have_char = true; - break; + return is_regexp; + } + + /* 2.b */ + if (ecma_is_value_true (is_regexp)) + { + /* 2.b.i */ + ecma_object_t *regexp_obj_p = ecma_get_object_from_value (regexp_arg); + ecma_value_t get_flags = ecma_op_object_get_by_magic_id (regexp_obj_p, LIT_MAGIC_STRING_FLAGS); + + if (ECMA_IS_VALUE_ERROR (get_flags)) + { + return get_flags; + } + + /* 2.b.ii */ + if (!ecma_op_require_object_coercible (get_flags)) + { + ecma_free_value (get_flags); + return ECMA_VALUE_ERROR; + } + + /* 2.b.iii */ + ecma_string_t *flags = ecma_op_to_string (get_flags); + + ecma_free_value (get_flags); + + if (JERRY_UNLIKELY (flags == NULL)) + { + return ECMA_VALUE_ERROR; + } + + uint16_t parsed_flag; + ecma_value_t flag_parse = ecma_regexp_parse_flags (flags, &parsed_flag); + + ecma_deref_ecma_string (flags); + + if (ECMA_IS_VALUE_ERROR (flag_parse)) + { + return flag_parse; + } + + if (!(parsed_flag & RE_FLAG_GLOBAL)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("RegExp argument should have global flag.")); + } + } + + /* 2.c */ + ecma_value_t matcher = ecma_op_get_method_by_symbol_id (regexp_arg, LIT_GLOBAL_SYMBOL_MATCH_ALL); + + if (ECMA_IS_VALUE_ERROR (matcher)) + { + return matcher; + } + + /* 2.d */ + if (!ecma_is_value_undefined (matcher)) + { + /* 2.d.i */ + ecma_object_t *matcher_method = ecma_get_object_from_value (matcher); + ecma_value_t result = ecma_op_function_call (matcher_method, regexp_arg, &this_argument, 1); + ecma_deref_object (matcher_method); + return result; } } - ECMA_FINALIZE_UTF8_STRING (start_p, start_size); + /* 3. */ + ecma_string_t *str_p = ecma_op_to_string (this_argument); - return have_char; -} /* ecma_find_char_in_string */ + if (JERRY_UNLIKELY (str_p == NULL)) + { + return ECMA_VALUE_ERROR; + } + + /* 4. */ + ecma_object_t *new_regexp_obj_p = ecma_op_regexp_alloc (NULL); + + if (JERRY_UNLIKELY (new_regexp_obj_p == NULL)) + { + ecma_deref_ecma_string (str_p); + return ECMA_VALUE_ERROR; + } + + ecma_value_t new_regexp = ecma_op_create_regexp_from_pattern (new_regexp_obj_p, regexp_arg, ECMA_VALUE_UNDEFINED); + + if (ECMA_IS_VALUE_ERROR (new_regexp)) + { + ecma_deref_ecma_string (str_p); + ecma_deref_object (new_regexp_obj_p); + return new_regexp; + } + + /* 5. */ + ecma_value_t string_arg = ecma_make_string_value (str_p); + ecma_value_t ret_value = ecma_op_invoke_by_symbol_id (new_regexp, LIT_GLOBAL_SYMBOL_MATCH_ALL, &string_arg, 1); + + ecma_deref_ecma_string (str_p); + ecma_free_value (new_regexp); + + return ret_value; +} /* ecma_builtin_string_prototype_object_match_all */ #endif /* ENABLED (JERRY_ESNEXT) */ /** @@ -469,7 +561,7 @@ ecma_builtin_string_prototype_object_replace_helper (ecma_value_t this_value, /* return ECMA_VALUE_ERROR; } - bool have_global_flag = ecma_find_char_in_string (flags, LIT_CHAR_LOWERCASE_G); + bool have_global_flag = lit_find_char_in_string (flags, LIT_CHAR_LOWERCASE_G); ecma_deref_ecma_string (flags); @@ -1393,6 +1485,13 @@ ecma_builtin_string_prototype_dispatch_routine (uint8_t builtin_routine_id, /**< { return ecma_builtin_string_prototype_object_match (this_arg, arg1); } + +#if ENABLED (JERRY_ESNEXT) + if (builtin_routine_id == ECMA_STRING_PROTOTYPE_MATCH_ALL) + { + return ecma_builtin_string_prototype_object_match_all (this_arg, arg1); + } +#endif /* ENABLED (JERRY_ESNEXT) */ #endif /* ENABLED (JERRY_BUILTIN_REGEXP) */ if (builtin_routine_id <= ECMA_STRING_PROTOTYPE_CHAR_CODE_AT) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.inc.h index 9a537fb05..39b45e178 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.inc.h @@ -79,6 +79,7 @@ ROUTINE (LIT_MAGIC_STRING_CODE_POINT_AT, ECMA_STRING_PROTOTYPE_CODE_POINT_AT, 1, ROUTINE (LIT_MAGIC_STRING_PAD_START, ECMA_STRING_PROTOTYPE_PAD_START, 2, 1) ROUTINE (LIT_MAGIC_STRING_PAD_END, ECMA_STRING_PROTOTYPE_PAD_END, 2, 1) ROUTINE (LIT_GLOBAL_SYMBOL_ITERATOR, ECMA_STRING_PROTOTYPE_ITERATOR, 0, 0) +ROUTINE (LIT_MAGIC_STRING_MATCH_ALL, ECMA_STRING_PROTOTYPE_MATCH_ALL, 1, 1) INTRINSIC_PROPERTY (LIT_MAGIC_STRING_TRIM_START, LIT_MAGIC_STRING_TRIM_START, ECMA_PROPERTY_CONFIGURABLE_WRITABLE) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-symbol.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-symbol.inc.h index 111c82596..0eeaf57cf 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-symbol.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-symbol.inc.h @@ -89,6 +89,10 @@ SYMBOL_VALUE (LIT_MAGIC_STRING_TO_STRING_TAG, SYMBOL_VALUE (LIT_MAGIC_STRING_UNSCOPABLES, LIT_GLOBAL_SYMBOL_UNSCOPABLES) +/* ECMA-262 v11, 19.4.2.8 */ +SYMBOL_VALUE (LIT_MAGIC_STRING_MATCH_ALL, + LIT_GLOBAL_SYMBOL_MATCH_ALL) + /* Routine properties: * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ ROUTINE (LIT_MAGIC_STRING_FOR, ecma_builtin_symbol_for, 1, 1) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h index 732c85c47..b5455174e 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h @@ -639,6 +639,13 @@ BUILTIN (ECMA_BUILTIN_ID_STRING_ITERATOR_PROTOTYPE, true, string_iterator_prototype) +/* The %RegExpStringIteratorPrototype% object (ECMA-262 v11, 21.2.7.1) */ +BUILTIN (ECMA_BUILTIN_ID_REGEXP_STRING_ITERATOR_PROTOTYPE, + ECMA_OBJECT_TYPE_GENERAL, + ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, + true, + regexp_string_iterator_prototype) + /* The %AsyncIteratorPrototype% object (ECMA-262 v10, 25.1.3) */ BUILTIN (ECMA_BUILTIN_ID_ASYNC_ITERATOR_PROTOTYPE, ECMA_OBJECT_TYPE_GENERAL, diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index 96648844a..9105cbd44 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -899,7 +899,8 @@ static const uint16_t ecma_global_symbol_descriptions[] = LIT_MAGIC_STRING_SPLIT, LIT_MAGIC_STRING_TO_PRIMITIVE, LIT_MAGIC_STRING_TO_STRING_TAG, - LIT_MAGIC_STRING_UNSCOPABLES + LIT_MAGIC_STRING_UNSCOPABLES, + LIT_MAGIC_STRING_MATCH_ALL, }; JERRY_STATIC_ASSERT (sizeof (ecma_global_symbol_descriptions) / sizeof (uint16_t) == ECMA_BUILTIN_GLOBAL_SYMBOL_COUNT, @@ -2657,6 +2658,7 @@ ecma_object_check_class_name_is_object (ecma_object_t *obj_p) /**< object */ || ecma_builtin_is (obj_p, ECMA_BUILTIN_ID_ARRAY_ITERATOR_PROTOTYPE) || ecma_builtin_is (obj_p, ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE) || ecma_builtin_is (obj_p, ECMA_BUILTIN_ID_STRING_ITERATOR_PROTOTYPE) + || ecma_builtin_is (obj_p, ECMA_BUILTIN_ID_REGEXP_STRING_ITERATOR_PROTOTYPE) || ecma_builtin_is (obj_p, ECMA_BUILTIN_ID_EVAL_ERROR_PROTOTYPE) || ecma_builtin_is (obj_p, ECMA_BUILTIN_ID_RANGE_ERROR_PROTOTYPE) || ecma_builtin_is (obj_p, ECMA_BUILTIN_ID_REFERENCE_ERROR_PROTOTYPE) @@ -2752,6 +2754,10 @@ ecma_object_get_class_name (ecma_object_t *obj_p) /**< object */ { return LIT_MAGIC_STRING_STRING_ITERATOR_UL; } + case ECMA_PSEUDO_REGEXP_STRING_ITERATOR: + { + return LIT_MAGIC_STRING_REGEXP_STRING_ITERATOR_UL; + } #endif /* ENABLED (JERRY_ESNEXT) */ default: { diff --git a/jerry-core/ecma/operations/ecma-regexp-object.h b/jerry-core/ecma/operations/ecma-regexp-object.h index 714ba04e1..5485830af 100644 --- a/jerry-core/ecma/operations/ecma-regexp-object.h +++ b/jerry-core/ecma/operations/ecma-regexp-object.h @@ -151,6 +151,19 @@ typedef struct uint8_t char_size; /**< size of encoded characters */ } ecma_regexp_ctx_t; +/** + * RegExpStringIterator object internal slots + * + * See also: + * ECMA-262 v11, 21.2.7.2 + */ +typedef struct +{ + ecma_extended_object_t header; /**< extended object part */ + ecma_value_t iterating_regexp; /**< [[IteratingRegExp]] internal slot */ + ecma_value_t iterated_string; /**< [[IteratedString]] internal slot */ +} ecma_regexp_string_iterator_t; + #if ENABLED (JERRY_ESNEXT) lit_code_point_t ecma_regexp_unicode_advance (const lit_utf8_byte_t **str_p, const lit_utf8_byte_t *end_p); #endif /* ENABLED (JERRY_ESNEXT) */ diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index b73193ccf..fe7d2ff2e 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -712,6 +712,7 @@ typedef enum JERRY_SYMBOL_TO_PRIMITIVE, /**< @@toPrimitive well-known symbol */ JERRY_SYMBOL_TO_STRING_TAG, /**< @@toStringTag well-known symbol */ JERRY_SYMBOL_UNSCOPABLES, /**< @@unscopables well-known symbol */ + JERRY_SYMBOL_MATCH_ALL, /**< @@matchAll well-known symbol */ } jerry_well_known_symbol_t; jerry_value_t jerry_get_well_known_symbol (jerry_well_known_symbol_t symbol); diff --git a/jerry-core/lit/lit-char-helpers.c b/jerry-core/lit/lit-char-helpers.c index f235de281..3805d7699 100644 --- a/jerry-core/lit/lit-char-helpers.c +++ b/jerry-core/lit/lit-char-helpers.c @@ -981,3 +981,36 @@ lit_char_fold_to_upper (lit_code_point_t cp) /**< code point */ #endif /* ENABLED (JERRY_UNICODE_CASE_CONVERSION) */ } /* lit_char_fold_to_upper */ #endif /* ENABLED (JERRY_ESNEXT) */ + +/** + * Helper method to find a specific character in a string + * + * Used by: + * ecma_builtin_string_prototype_object_replace_helper + * + * @return true - if the given character is in the string + * false - otherwise + */ +bool +lit_find_char_in_string (ecma_string_t *str_p, /**< source string */ + lit_utf8_byte_t c) /**< character to find*/ +{ + ECMA_STRING_TO_UTF8_STRING (str_p, start_p, start_size); + + const lit_utf8_byte_t *str_curr_p = start_p; + const lit_utf8_byte_t *str_end_p = start_p + start_size; + bool have_char = false; + + while (str_curr_p < str_end_p) + { + if (*str_curr_p++ == c) + { + have_char = true; + break; + } + } + + ECMA_FINALIZE_UTF8_STRING (start_p, start_size); + + return have_char; +} /* lit_find_char_in_string */ diff --git a/jerry-core/lit/lit-char-helpers.h b/jerry-core/lit/lit-char-helpers.h index e4e0c7ee7..17c5f6e1e 100644 --- a/jerry-core/lit/lit-char-helpers.h +++ b/jerry-core/lit/lit-char-helpers.h @@ -231,6 +231,7 @@ size_t lit_code_point_get_cesu8_length (lit_code_point_t code_point); void lit_four_byte_utf8_char_to_cesu8 (uint8_t *dst_p, const uint8_t *source_p); uint32_t lit_char_hex_lookup (const lit_utf8_byte_t *buf_p, const lit_utf8_byte_t *const buf_end_p, uint32_t lookup); uint32_t lit_parse_decimal (const lit_utf8_byte_t **buffer_p, const lit_utf8_byte_t *const buffer_end_p); +bool lit_find_char_in_string (ecma_string_t *str_p, lit_utf8_byte_t c); /** * Null character diff --git a/jerry-core/lit/lit-magic-strings.h b/jerry-core/lit/lit-magic-strings.h index 0bcf9df20..6b6743d6b 100644 --- a/jerry-core/lit/lit-magic-strings.h +++ b/jerry-core/lit/lit-magic-strings.h @@ -55,7 +55,8 @@ typedef enum LIT_GLOBAL_SYMBOL_TO_PRIMITIVE, /**< @@toPrimitive well known symbol */ LIT_GLOBAL_SYMBOL_TO_STRING_TAG, /**< @@toStringTag well known symbol */ LIT_GLOBAL_SYMBOL_UNSCOPABLES, /**< @@unscopables well known symbol */ - LIT_GLOBAL_SYMBOL__LAST = LIT_GLOBAL_SYMBOL_UNSCOPABLES, /**< last global symbol */ + LIT_GLOBAL_SYMBOL_MATCH_ALL, /**< @@matchAll well known symbol */ + LIT_GLOBAL_SYMBOL__LAST = LIT_GLOBAL_SYMBOL_MATCH_ALL, /**< last global symbol */ LIT_INTERNAL_MAGIC_STRING_DELETED, /**< special value for deleted properties */ LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT, /**< Internal object ID for internal properties */ diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 0cf75ef35..66f2157eb 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -585,6 +585,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_FROZEN_UL, "isFrozen") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_SEALED_UL, "isSealed") #if ENABLED (JERRY_ESNEXT) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ITERATOR, "iterator") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MATCH_ALL, "matchAll") #endif #if ENABLED (JERRY_BUILTIN_STRING) && ENABLED (JERRY_ESNEXT) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PAD_START, "padStart") @@ -661,7 +662,8 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_UINT32_UL, "getUint32") #if ENABLED (JERRY_ESNEXT) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_IS_INTEGER, "isInteger") #endif -#if ENABLED (JERRY_BUILTIN_REGEXP) +#if ENABLED (JERRY_BUILTIN_REGEXP) \ +|| ENABLED (JERRY_ESNEXT) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_LASTINDEX_UL, "lastIndex") #endif #if !ENABLED (JERRY_ESNEXT) && ENABLED (JERRY_BUILTIN_REGEXP) \ @@ -978,6 +980,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL, "propertyIsEnu #if ENABLED (JERRY_ESNEXT) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL, "getOwnPropertySymbols") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL, "AsyncGeneratorFunction") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REGEXP_STRING_ITERATOR_UL, "RegExp String Iterator") #endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL, "getOwnPropertyDescriptor") #if ENABLED (JERRY_ESNEXT) diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index bec5d3d79..c9cd3b882 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -219,6 +219,7 @@ LIT_MAGIC_STRING_RUNNABLE_UL = "Runnable" LIT_MAGIC_STRING_DATAVIEW_UL = "DataView" LIT_MAGIC_STRING_FUNCTION_UL = "Function" LIT_MAGIC_STRING_INFINITY_UL = "Infinity" +LIT_MAGIC_STRING_MATCH_ALL = "matchAll" LIT_MAGIC_STRING_URI_ERROR_UL = "URIError" LIT_MAGIC_STRING_ENDS_WITH = "endsWith" LIT_MAGIC_STRING_FUNCTION = "function" @@ -394,6 +395,7 @@ LIT_MAGIC_STRING_TO_LOCALE_TIME_STRING_UL = "toLocaleTimeString" LIT_MAGIC_STRING_GET_OWN_PROPERTY_NAMES_UL = "getOwnPropertyNames" LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL = "propertyIsEnumerable" LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL = "getOwnPropertySymbols" +LIT_MAGIC_STRING_REGEXP_STRING_ITERATOR_UL = "RegExp String Iterator" LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL = "AsyncGeneratorFunction" LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL = "getOwnPropertyDescriptor" LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTORS_UL = "getOwnPropertyDescriptors" diff --git a/tests/jerry/es.next/regexp-prototype-match-all.js b/tests/jerry/es.next/regexp-prototype-match-all.js new file mode 100644 index 000000000..a6945dd4d --- /dev/null +++ b/tests/jerry/es.next/regexp-prototype-match-all.js @@ -0,0 +1,190 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// 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(RegExp.prototype[Symbol.matchAll].length === 1); +var desc = Object.getOwnPropertyDescriptor(RegExp.prototype[Symbol.matchAll], "length"); +assert(!desc.enumerable); +assert(!desc.writable); +assert(desc.configurable); + +assert(RegExp.prototype[Symbol.matchAll].name === "[Symbol.matchAll]"); +var desc = Object.getOwnPropertyDescriptor(RegExp.prototype[Symbol.matchAll], "name"); +assert(!desc.enumerable); +assert(!desc.writable); +assert(desc.configurable); + +// test basic functionality +var re = /[0-9]+/g; +var str = '2016-01-02'; +var result = re[Symbol.matchAll](str); +assert(Array.from(result, x => x[0]).toString() === "2016,01,02"); + +class MyRegExp extends RegExp { + [Symbol.matchAll](str) { + const result = RegExp.prototype[Symbol.matchAll].call(this, str); + if (!result) { + return null; + } + return Array.from(result); + } +} + +var regexp = new MyRegExp('-[0-9]+', 'g'); +var result = re[Symbol.matchAll]("2016-01-02|2019-03-07"); +assert(Array.from(result, x => x[0]).toString() === "2016,01,02,2019,03,07"); + +var counter = 0; +var callArgs; +var regexp = /\d/u; +regexp.constructor = { + [Symbol.species]: function(){ + counter++; + callArgs = arguments; + return /\w/g; + } +}; + +var str = 'a*b'; +var result = regexp[Symbol.matchAll](str); + +assert(counter === 1); +assert(callArgs.length === 2); +assert(callArgs[0] === regexp); +assert(callArgs[1] === 'u'); +assert(Array.from(result, x => x[0]).toString() === "a"); + +// test when flags throws error +var regexp = /a/g; +Object.defineProperty(regexp, 'flags', { get () { throw 42; }}); + +try { + regexp[Symbol.matchAll]("foo"); + assert(false); +} catch (e) { + assert(e === 42); +} + +// test when flags value is symbol +var regexp = /a/g; +var sym = Symbol("foo") +Object.defineProperty(regexp, 'flags', { value: sym }); + +try { + regexp[Symbol.matchAll]("foo"); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +// test when match Symbol throws error +var regexp = /[A-Z]/g; +Object.defineProperty (regexp, Symbol.match, { get () { throw 42; }}); + +try { + regexp[Symbol.matchAll]("foo"); + assert(false); +} catch (e) { + assert(e === 42); +} + +// test when can't create RegExp +var obj = { + toString() { + throw 42; + } +}; + +try { + RegExp.prototype[Symbol.matchAll].call(obj, ''); + assert(false); +} catch (e) { + assert(e === 42); +} + +// test when constructor throws error +var regexp = /./; +Object.defineProperty(regexp, 'constructor', { + get(){ + throw 42; + } +}); + +try { + regexp[Symbol.matchAll]("foo"); + assert(false); +} catch (e) { + assert(e === 42); +} + +// test when global flag throws error +var regexp = /[A-Z]/; +Object.defineProperty(regexp, 'global', { get() { throw 42; }}); + +try { + regexp[Symbol.matchAll](''); + assert(false); +} catch (e) { + assert(e === 42); +} + +// test when lastIndex throws error +var regexp = /[A-Z]/; +regexp.lastIndex = { + valueOf() { + throw 42; + } +}; + +try { + regexp[Symbol.matchAll]("foo"); + assert(false); +} catch (e) { + assert(e === 42); +} + +// test when argument is not an object +try { + RegExp.prototype[Symbol.matchAll].call(null, ''); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + RegExp.prototype[Symbol.matchAll].call(true, ''); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + RegExp.prototype[Symbol.matchAll].call('', ''); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + RegExp.prototype[Symbol.matchAll].call(Symbol(), ''); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + RegExp.prototype[Symbol.matchAll].call(1, ''); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} diff --git a/tests/jerry/es.next/string-prototype-match-all.js b/tests/jerry/es.next/string-prototype-match-all.js new file mode 100644 index 000000000..a09174a26 --- /dev/null +++ b/tests/jerry/es.next/string-prototype-match-all.js @@ -0,0 +1,177 @@ +// Copyright JS Foundation and other contributors, http://js.foundation +// +// 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(String.prototype.matchAll.length === 1); +var desc = Object.getOwnPropertyDescriptor(String.prototype.matchAll, "length"); +assert(!desc.enumerable); +assert(!desc.writable); +assert(desc.configurable); + +assert(String.prototype.matchAll.name === "matchAll"); +var desc = Object.getOwnPropertyDescriptor(String.prototype.matchAll, "name"); +assert(!desc.enumerable); +assert(!desc.writable); +assert(desc.configurable); + +// test basic functionality +var re = /[0-9]+/g; +var str = '2016-01-02|2019-03-07'; +var result = str.matchAll(re); +var result_array = Array.from(result, x => x[0]); +assert(result_array.toString() === "2016,01,02,2019,03,07"); + +var counter = 0; +var obj = {}; +RegExp.prototype[Symbol.matchAll] = function() { + counter++; + return obj; +}; + +assert('a'.matchAll(null) === obj); +assert(counter === 1); + +assert(''.matchAll(undefined) === obj); +assert(counter === 2) ; + +var obj = {}; +var retval = {}; +var counter = 0; +var thisVal, args; + +obj[Symbol.matchAll] = function () { + counter++; + thisVal = this; + args = arguments; + return retval; +}; + +var str = '' + +assert(str.matchAll(obj) === retval); +assert(counter === 1); +assert(thisVal === obj); +assert(args !== undefined); +assert(args.length === 1); +assert(args[0] === str); + +// throw error when flag is not global +try { + "foo".matchAll(/a/); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + "foo".matchAll(/a/i); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + "foo".matchAll(/a/m); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + "foo".matchAll(/a/u); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + "foo".matchAll(/a/u); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + "foo".matchAll(/a/y); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +var regexp = /a/; +Object.defineProperty(regexp, 'flags', { + value: 'muyi' +}); + +try { + "foo".matchAll(regexp); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +// test when flags value is undefined +var regexp = /a/g; +Object.defineProperty(regexp, 'flags', { value: undefined }); + +try { + "foo".matchAll(regexp) + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +// test when match Symbol throws error +var regexp = /[A-Z]/g; +Object.defineProperty (regexp, Symbol.match, { get () { throw 42; }}); + +try { + "foo".matchAll(regexp); + assert(false); +} catch (e) { + assert(e === 42); +} + +// test when flags throws error +var regexp = /a/g; +Object.defineProperty(regexp, 'flags', { get () { throw 42; }}); + +try { + "foo".matchAll(regexp); + assert(false); +} catch (e) { + assert(e === 42); +} + +// test when flags value is symbol +var regexp = /a/g; +var sym = Symbol("foo") +Object.defineProperty(regexp, 'flags', { value: sym }); + +try { + "foo".matchAll(regexp); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +// test when matchAll Symbol throws error +var regexp = /[A-Z]/g; +Object.defineProperty (regexp, Symbol.matchAll, { get () { throw 42; }}); + +try { + "foo".matchAll(regexp); + assert(false); +} catch (e) { + assert(e === 42); +} diff --git a/tests/test262-esnext-excludelist.xml b/tests/test262-esnext-excludelist.xml index 36f2bb80c..3d6116994 100644 --- a/tests/test262-esnext-excludelist.xml +++ b/tests/test262-esnext-excludelist.xml @@ -109,62 +109,8 @@ Test expects incorrect call order - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -178,7 +124,6 @@ - diff --git a/tests/unit-core/test-symbol.c b/tests/unit-core/test-symbol.c index 33413ac2b..c88593f30 100644 --- a/tests/unit-core/test-symbol.c +++ b/tests/unit-core/test-symbol.c @@ -232,6 +232,7 @@ main (void) " [Symbol.toPrimitive]: 10," " [Symbol.toStringTag]: 11," " [Symbol.unscopables]: 12," + " [Symbol.matchAll]: 13," "})"; const char *symbols[] = @@ -248,6 +249,7 @@ main (void) "toPrimitive", "toStringTag", "unscopables", + "matchAll", }; jerry_value_t obj = jerry_eval (obj_src, sizeof (obj_src) - 1, JERRY_PARSE_NO_OPTS); @@ -262,7 +264,7 @@ main (void) uint32_t prop_index = 0; for (jerry_well_known_symbol_t id = JERRY_SYMBOL_ASYNC_ITERATOR; - id <= JERRY_SYMBOL_UNSCOPABLES; + id <= JERRY_SYMBOL_MATCH_ALL; id++, expected++, prop_index++) { jerry_value_t well_known_symbol = jerry_get_well_known_symbol (id); @@ -316,7 +318,7 @@ main (void) prop_index = 0; for (jerry_well_known_symbol_t id = JERRY_SYMBOL_ASYNC_ITERATOR; - id <= JERRY_SYMBOL_UNSCOPABLES; + id <= JERRY_SYMBOL_MATCH_ALL; id++, expected++, prop_index++) { jerry_value_t well_known_symbol = jerry_get_well_known_symbol (id); @@ -330,7 +332,7 @@ main (void) jerry_release_value (well_known_symbol); } - jerry_well_known_symbol_t invalid_symbol = (jerry_well_known_symbol_t) (JERRY_SYMBOL_UNSCOPABLES + 1); + jerry_well_known_symbol_t invalid_symbol = (jerry_well_known_symbol_t) (JERRY_SYMBOL_MATCH_ALL + 1); jerry_value_t invalid_well_known_symbol = jerry_get_well_known_symbol (invalid_symbol); TEST_ASSERT (jerry_value_is_undefined (invalid_well_known_symbol)); jerry_release_value (invalid_well_known_symbol);