diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c index 1eb19d225..eaa5ca2ef 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.c @@ -569,12 +569,20 @@ ecma_builtin_helper_string_prototype_object_index_of (ecma_string_t *original_st #if ENABLED (JERRY_ES2015_BUILTIN) /* 4, 6 (startsWith, includes, endsWith) */ - if (mode >= ECMA_STRING_STARTS_WITH - && (ecma_is_value_object (arg1) - && ecma_object_class_is (ecma_get_object_from_value (arg1), LIT_MAGIC_STRING_REGEXP_UL))) + if (mode >= ECMA_STRING_STARTS_WITH) { - JERRY_ASSERT (ECMA_STRING_LAST_INDEX_OF < mode && mode <= ECMA_STRING_ENDS_WITH); - return ecma_raise_type_error (ECMA_ERR_MSG ("Search string can't be of type: RegExp")); + ecma_value_t regexp = ecma_op_is_regexp (arg1); + + if (ECMA_IS_VALUE_ERROR (regexp)) + { + return regexp; + } + + if (regexp == ECMA_VALUE_TRUE) + { + JERRY_ASSERT (ECMA_STRING_LAST_INDEX_OF < mode && mode <= ECMA_STRING_ENDS_WITH); + return ecma_raise_type_error (ECMA_ERR_MSG ("Search string can't be of type: RegExp")); + } } #endif /* ENABLED (JERRY_ES2015_BUILTIN) */ 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 eb6c8b07c..c211b44d7 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp-prototype.c @@ -60,8 +60,7 @@ static ecma_value_t ecma_builtin_regexp_prototype_flags_helper (ecma_value_t this, /**< this value */ uint16_t *flags_p) /**< [out] flags */ { - if (!ecma_is_value_object (this) - || !ecma_object_class_is (ecma_get_object_from_value (this), LIT_MAGIC_STRING_REGEXP_UL)) + if (!ecma_object_is_regexp_object (this)) { return ecma_raise_type_error (ECMA_ERR_MSG ("Incompatible type")); } @@ -133,8 +132,7 @@ ecma_builtin_regexp_prototype_get_flags (ecma_value_t this_arg) /**< this argume static ecma_value_t ecma_builtin_regexp_prototype_get_source (ecma_value_t this_arg) /**< this argument */ { - if (!ecma_is_value_object (this_arg) - || !ecma_object_class_is (ecma_get_object_from_value (this_arg), LIT_MAGIC_STRING_REGEXP_UL)) + if (!ecma_object_is_regexp_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_MSG ("Incompatible type")); } @@ -246,8 +244,7 @@ ecma_builtin_regexp_prototype_compile (ecma_value_t this_arg, /**< this argument ecma_value_t pattern_arg, /**< pattern or RegExp object */ ecma_value_t flags_arg) /**< flags */ { - if (!ecma_is_value_object (this_arg) - || !ecma_object_class_is (ecma_get_object_from_value (this_arg), LIT_MAGIC_STRING_REGEXP_UL) + if (!ecma_object_is_regexp_object (this_arg) /* The builtin RegExp.prototype object does not have [[RegExpMatcher]] internal slot */ || ecma_get_object_from_value (this_arg) == ecma_builtin_get (ECMA_BUILTIN_ID_REGEXP_PROTOTYPE)) { @@ -256,8 +253,7 @@ ecma_builtin_regexp_prototype_compile (ecma_value_t this_arg, /**< this argument uint16_t flags = 0; - if (ecma_is_value_object (pattern_arg) - && ecma_object_class_is (ecma_get_object_from_value (pattern_arg), LIT_MAGIC_STRING_REGEXP_UL) + if (ecma_object_is_regexp_object (pattern_arg) && ecma_get_object_from_value (pattern_arg) != ecma_builtin_get (ECMA_BUILTIN_ID_REGEXP_PROTOTYPE)) { if (!ecma_is_value_undefined (flags_arg)) @@ -357,8 +353,7 @@ static ecma_value_t ecma_builtin_regexp_prototype_exec (ecma_value_t this_arg, /**< this argument */ ecma_value_t arg) /**< routine's argument */ { - if (!ecma_is_value_object (this_arg) - || !ecma_object_class_is (ecma_get_object_from_value (this_arg), LIT_MAGIC_STRING_REGEXP_UL)) + if (!ecma_object_is_regexp_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_MSG ("Incomplete RegExp type")); } @@ -424,8 +419,7 @@ ecma_builtin_regexp_prototype_test (ecma_value_t this_arg, /**< this argument */ static ecma_value_t ecma_builtin_regexp_prototype_to_string (ecma_value_t this_arg) /**< this argument */ { - if (!ecma_is_value_object (this_arg) - || !ecma_object_class_is (ecma_get_object_from_value (this_arg), LIT_MAGIC_STRING_REGEXP_UL)) + if (!ecma_object_is_regexp_object (this_arg)) { return ecma_raise_type_error (ECMA_ERR_MSG ("Incompatible type")); } diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp.c b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp.c index 1acaa59a2..c91fa3fc8 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-regexp.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-regexp.c @@ -66,6 +66,10 @@ ecma_builtin_regexp_dispatch_construct (const ecma_value_t *arguments_list_p, /* { ecma_value_t pattern_value = ECMA_VALUE_UNDEFINED; ecma_value_t flags_value = ECMA_VALUE_UNDEFINED; +#if ENABLED (JERRY_ES2015) + ecma_value_t new_pattern = ECMA_VALUE_EMPTY; + ecma_value_t new_flags = ECMA_VALUE_EMPTY; +#endif /* ENABLED (JERRY_ES2015) */ if (arguments_list_len > 0) { @@ -78,26 +82,64 @@ ecma_builtin_regexp_dispatch_construct (const ecma_value_t *arguments_list_p, /* } } - if (ecma_is_value_object (pattern_value) - && ecma_object_class_is (ecma_get_object_from_value (pattern_value), LIT_MAGIC_STRING_REGEXP_UL)) +#if ENABLED (JERRY_ES2015) + ecma_value_t regexp_value = ecma_op_is_regexp (pattern_value); + + if (ECMA_IS_VALUE_ERROR (regexp_value)) { + return regexp_value; + } + + bool is_regexp = regexp_value == ECMA_VALUE_TRUE; +#else /* !ENABLED (JERRY_ES2015) */ + bool is_regexp = ecma_object_is_regexp_object (pattern_value); +#endif /* ENABLED (JERRY_ES2015) */ + + if (is_regexp) + { +#if ENABLED (JERRY_ES2015) + ecma_object_t *pattern_obj_p = ecma_get_object_from_value (pattern_value); + + new_pattern = ecma_op_object_get_by_magic_id (pattern_obj_p, LIT_MAGIC_STRING_SOURCE); + + if (ECMA_IS_VALUE_ERROR (new_pattern)) + { + return new_pattern; + } + + pattern_value = new_pattern; + + if (ecma_is_value_undefined (flags_value)) + { + new_flags = ecma_op_object_get_by_magic_id (pattern_obj_p, LIT_MAGIC_STRING_FLAGS); + + if (ECMA_IS_VALUE_ERROR (new_flags)) + { + ecma_free_value (new_pattern); + return new_flags; + } + + flags_value = new_flags; + } +#else /* !ENABLED (JERRY_ES2015) */ if (ecma_is_value_undefined (flags_value)) { return ecma_copy_value (pattern_value); } return ecma_raise_type_error (ECMA_ERR_MSG ("Invalid argument of RegExp call.")); +#endif /* ENABLED (JERRY_ES2015) */ } ecma_string_t *pattern_string_p = ecma_regexp_read_pattern_str_helper (pattern_value); + ecma_value_t ret_value = ECMA_VALUE_ERROR; if (pattern_string_p == NULL) { - return ECMA_VALUE_ERROR; + goto cleanup; } uint16_t flags = 0; - ecma_value_t ret_value = ECMA_VALUE_EMPTY; if (!ecma_is_value_undefined (flags_value)) { @@ -106,7 +148,7 @@ ecma_builtin_regexp_dispatch_construct (const ecma_value_t *arguments_list_p, /* if (JERRY_UNLIKELY (flags_string_p == NULL)) { ecma_deref_ecma_string (pattern_string_p); - return ECMA_VALUE_ERROR; + goto cleanup; } ret_value = ecma_regexp_parse_flags (flags_string_p, &flags); @@ -115,13 +157,20 @@ ecma_builtin_regexp_dispatch_construct (const ecma_value_t *arguments_list_p, /* if (ECMA_IS_VALUE_ERROR (ret_value)) { ecma_deref_ecma_string (pattern_string_p); - return ret_value; + goto cleanup; } JERRY_ASSERT (ecma_is_value_empty (ret_value)); } ret_value = ecma_op_create_regexp_object (pattern_string_p, flags); ecma_deref_ecma_string (pattern_string_p); + +cleanup: +#if ENABLED (JERRY_ES2015) + ecma_free_value (new_pattern); + ecma_free_value (new_flags); +#endif /* ENABLED (JERRY_ES2015) */ + return ret_value; } /* ecma_builtin_regexp_dispatch_construct */ 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 77e80738f..9bf2a0468 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-string-prototype.c @@ -286,8 +286,7 @@ ecma_builtin_string_prepare_search (ecma_value_t regexp_arg, /**< regex argument ecma_value_t *regexp_value) /**< [out] ptr to store the regexp object */ { /* 3. */ - if (ecma_is_value_object (regexp_arg) - && ecma_object_class_is (ecma_get_object_from_value (regexp_arg), LIT_MAGIC_STRING_REGEXP_UL)) + if (ecma_object_is_regexp_object (regexp_arg)) { *regexp_value = ecma_copy_value (regexp_arg); return ECMA_VALUE_EMPTY; @@ -539,8 +538,7 @@ ecma_builtin_string_prototype_object_replace (ecma_value_t this_value, /**< this } } #else /* !ENABLED (JERRY_ES2015) */ - if (ecma_is_value_object (search_value) - && ecma_object_class_is (ecma_get_object_from_value (search_value), LIT_MAGIC_STRING_REGEXP_UL)) + if (ecma_object_is_regexp_object (search_value)) { return ecma_regexp_replace_helper (search_value, this_value, replace_value); } @@ -866,8 +864,7 @@ ecma_builtin_string_prototype_object_split (ecma_value_t this_to_string_val, /** bool separator_is_regexp = false; - if (ecma_is_value_object (arg1) - && ecma_object_class_is (ecma_get_object_from_value (arg1), LIT_MAGIC_STRING_REGEXP_UL)) + if (ecma_object_is_regexp_object (arg1)) { separator_is_regexp = true; separator = ecma_copy_value (arg1); diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index 785d03bd4..faa2c9a5a 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -2475,6 +2475,19 @@ ecma_object_class_is (ecma_object_t *object_p, /**< object */ return false; } /* ecma_object_class_is */ +/** + * Checks if the given argument has [[RegExpMatcher]] internal slot + * + * @return true - if the given argument is a regexp + * false - otherwise + */ +inline bool JERRY_ATTR_ALWAYS_INLINE +ecma_object_is_regexp_object (ecma_value_t arg) /**< argument */ +{ + return (ecma_is_value_object (arg) + && ecma_object_class_is (ecma_get_object_from_value (arg), LIT_MAGIC_STRING_REGEXP_UL)); +} /* ecma_object_is_regexp_object */ + #if ENABLED (JERRY_ES2015) /** * Object's IsConcatSpreadable operation, used for Array.prototype.concat @@ -2510,6 +2523,40 @@ ecma_op_is_concat_spreadable (ecma_value_t arg) /**< argument */ return (ecma_make_boolean_value (ecma_is_value_array (arg))); } /* ecma_op_is_concat_spreadable */ + +/** + * IsRegExp operation + * + * See also: + * ECMA-262 v6, 22.1.3.1.1; + * + * @return ECMA_VALUE_ERROR - if the operation fails + * ECMA_VALUE_TRUE - if the argument is regexp + * ECMA_VALUE_FALSE - otherwise + */ +ecma_value_t +ecma_op_is_regexp (ecma_value_t arg) /**< argument */ +{ + if (!ecma_is_value_object (arg)) + { + return ECMA_VALUE_FALSE; + } + + ecma_value_t is_regexp = ecma_op_object_get_by_symbol_id (ecma_get_object_from_value (arg), + LIT_MAGIC_STRING_MATCH); + + if (ECMA_IS_VALUE_ERROR (is_regexp)) + { + return is_regexp; + } + + if (!ecma_is_value_undefined (is_regexp)) + { + return ecma_make_boolean_value (ecma_op_to_boolean (is_regexp)); + } + + return ecma_make_boolean_value (ecma_object_is_regexp_object (arg)); +} /* ecma_op_is_regexp */ #endif /* ENABLED (JERRY_ES2015) */ /** diff --git a/jerry-core/ecma/operations/ecma-objects.h b/jerry-core/ecma/operations/ecma-objects.h index 4421d6adf..b323da239 100644 --- a/jerry-core/ecma/operations/ecma-objects.h +++ b/jerry-core/ecma/operations/ecma-objects.h @@ -65,9 +65,11 @@ ecma_collection_t * ecma_op_object_get_property_names (ecma_object_t *obj_p, uin lit_magic_string_id_t ecma_object_get_class_name (ecma_object_t *obj_p); bool ecma_object_class_is (ecma_object_t *object_p, uint32_t class_id); +bool ecma_object_is_regexp_object (ecma_value_t arg); #if ENABLED (JERRY_ES2015) ecma_value_t ecma_op_is_concat_spreadable (ecma_value_t arg); -#endif /* ENABLED (JERRY_ES2015) */ +ecma_value_t ecma_op_is_regexp (ecma_value_t arg); +#endif /* !ENABLED (JERRY_ES2015) */ /** * @} diff --git a/tests/jerry/es2015/regexp-construct.js b/tests/jerry/es2015/regexp-construct.js new file mode 100644 index 000000000..48599a931 --- /dev/null +++ b/tests/jerry/es2015/regexp-construct.js @@ -0,0 +1,30 @@ +// 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. + +var r = RegExp ("a","gim"); +var r2 = RegExp (r,"gim"); + +assert(r2.source === 'a'); +assert(r2.global === true); +assert(r2.ignoreCase === true); +assert(r2.multiline === true); + +var obj = { get source() { throw 5 }, [Symbol.match] : true } + +try { + new RegExp (obj); + assert(false) +} catch (e) { + assert(e === 5); +} diff --git a/tests/jerry/es2015/string-prototype-endswith.js b/tests/jerry/es2015/string-prototype-endswith.js index a08e9cfc2..b92ccdc0d 100644 --- a/tests/jerry/es2015/string-prototype-endswith.js +++ b/tests/jerry/es2015/string-prototype-endswith.js @@ -61,3 +61,20 @@ try { } catch (e) { assert (e instanceof TypeError); } + +z[Symbol.match] = false; +assert(y.endsWith(z) === false); + +try { + "foo".endsWith({[Symbol.match] : true}); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + "foo".endsWith({get [Symbol.match] () { throw 5}}); + assert(false); +} catch (e) { + assert(e === 5); +} diff --git a/tests/jerry/es2015/string-prototype-includes.js b/tests/jerry/es2015/string-prototype-includes.js index 36508b2ca..1fdb0ef82 100644 --- a/tests/jerry/es2015/string-prototype-includes.js +++ b/tests/jerry/es2015/string-prototype-includes.js @@ -60,3 +60,20 @@ try { } catch (e) { assert (e instanceof TypeError); } + +z[Symbol.match] = false; +assert(y.includes(z) === false); + +try { + "foo".includes({[Symbol.match] : true}); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + "foo".includes({get [Symbol.match] () { throw 5}}); + assert(false); +} catch (e) { + assert(e === 5); +} diff --git a/tests/jerry/es2015/string-prototype-startswith.js b/tests/jerry/es2015/string-prototype-startswith.js index 8926ce671..0effd36b4 100644 --- a/tests/jerry/es2015/string-prototype-startswith.js +++ b/tests/jerry/es2015/string-prototype-startswith.js @@ -51,3 +51,20 @@ try { } catch (e) { assert (e instanceof TypeError); } + +y[Symbol.match] = false; +assert(x.startsWith(y) === false); + +try { + "foo".startsWith({[Symbol.match] : true}); + assert(false); +} catch (e) { + assert(e instanceof TypeError); +} + +try { + "foo".startsWith({get [Symbol.match] () { throw 5}}); + assert(false); +} catch (e) { + assert(e === 5); +} diff --git a/tests/jerry/es5.1/regexp-construct.js b/tests/jerry/es5.1/regexp-construct.js new file mode 100644 index 000000000..6f20bb126 --- /dev/null +++ b/tests/jerry/es5.1/regexp-construct.js @@ -0,0 +1,25 @@ +// 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. + +var r = RegExp ("a","gim"); +var r2; + +try { + r2 = RegExp (r,"gim"); + assert(false); +} +catch ( e ) +{ + assert (e instanceof TypeError); +} diff --git a/tests/jerry/regexp-construct.js b/tests/jerry/regexp-construct.js index 4aa7c8074..5fe4b0d97 100644 --- a/tests/jerry/regexp-construct.js +++ b/tests/jerry/regexp-construct.js @@ -45,14 +45,6 @@ assert (r.ignoreCase == true); assert (r.multiline == true); var r2; -try { - r2 = RegExp (r,"gim"); - assert(false); -} -catch ( e ) -{ - assert (e instanceof TypeError); -} r2 = RegExp (r); assert (r2.source == "a");